This document is the summary of the R for Data Analysis workshop.

All correspondence related to this document should be addressed to:

Omid Ghasemi (Macquarie University, Sydney, NSW, 2109, AUSTRALIA)

Email:

1 Introduction to R

1.1 Basics and Variables

R can be used as a calculator. For mathematical purposes, be careful of the order in which R executes the commands.

10 + 10
## [1] 20
4 ^ 2
## [1] 16
(250 / 500) * 100
## [1] 50

R is a bit flexible with spacing (but no spacing in the name of variables and words)

10+10
## [1] 20
10                 +           10
## [1] 20

R can sometimes tell that you’re not finished yet

10 +

How to create a variable? Variable assignment using <- and =. Note that R is case sensitive for everything

pay <- 250

month = 12

pay * month
## [1] 3000
salary <- pay * month

Few points in naming variables and vectors: use short, informative words, keep same method (e.g., you can use capital letters but it is not recommended, use only _ or . ).

1.2 Function

Function is a set of statements combined together to perform a specific task. When we use a block of code repeatedly, we can convert it to a function. To write a function, first, you need to define it:

my_multiplier <- function(a,b){
  result = a * b
  return (result)
}

This code do nothing. To get a result, you need to call it:

my_multiplier (a=2, b=4)
## [1] 8
# or: my_multiplier (2, 4)

We can set a default value for our arguments:

my_multiplier2 <- function(a,b=4){
  result = a * b
  return (result)
}

my_multiplier2 (a=2)
## [1] 8
# or: my_multiplier (2)
# or: my_multiplier (2, 6)

Fortunately, you do not need to write everything from scratch. R has lots of built-in functions that you can use:

round(54.6787)
## [1] 55
round(54.5787, digits = 2)
## [1] 54.58

Use ? before the function name to get some help. For example, ?round. You will see many functions in the rest of the workshop.

1.3 Data Types

function class() is used to show what is the type of a variable.

  1. Logical: TRUE, FALSE can be abbreviated as T, F. They has to be capital, ‘true’ is not a logical data:
class(TRUE)
## [1] "logical"
class(F)
## [1] "logical"
  1. Numeric: all numbers e.g. 5, 10.5, 11,37; a special type of numeric is “integer” which is numbers without decimal. Integers are always numeric, but numeric is not always integer:
class(2)
## [1] "numeric"
class(13.46)
## [1] "numeric"
  1. Character: text for example, “I love R” or “4” or “4.5”:
class("ha ha ha ha")
## [1] "character"
class("56.6")
## [1] "character"
class("TRUE")
## [1] "character"

Can we change the type of data in a variable? Yes, you need to use the function as.---()

as.numeric(TRUE)
## [1] 1
as.character(4)
## [1] "4"
as.numeric("4.5")
## [1] 4.5
as.numeric("Hello")
## Warning: NAs introduced by coercion
## [1] NA

1.4 Data Structures

1.4.1 Vector

When there are more than one number or letter stored. Use the combine function c() for that.

sale <- c(1, 2, 3,4, 5, 6, 7, 8, 9, 10) # also sale <- c(1:10)

sale <- c(1:10)

sale * sale
##  [1]   1   4   9  16  25  36  49  64  81 100

Subsetting a vector:

days <- c("Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday")

days[2]
## [1] "Sunday"
days[-2]
## [1] "Saturday"  "Monday"    "Tuesday"   "Wednesday" "Thursday"  "Friday"
days[c(2, 3, 4)]
## [1] "Sunday"  "Monday"  "Tuesday"
  • Exercise: Create a vector named my_vector with numbers from 0 to 1000 in it and calculate mean, median, sd, min, max, and sum of that vector:
my_vector <- (0:1000)

mean(my_vector)
## [1] 500
median(my_vector)
## [1] 500
min(my_vector)
## [1] 0
range(my_vector)
## [1]    0 1000
class(my_vector)
## [1] "integer"
sum(my_vector)
## [1] 500500
sd(my_vector)
## [1] 289.1081

1.4.2 List

List allows you to gather a variety of objects under one name (that is, the name of the list) in an ordered way. These objects can be matrices, vectors, data frames, even other list.

my_list = list(sale, 1, 3, 4:7, "HELLO", "hello", FALSE)
my_list
## [[1]]
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## [[2]]
## [1] 1
## 
## [[3]]
## [1] 3
## 
## [[4]]
## [1] 4 5 6 7
## 
## [[5]]
## [1] "HELLO"
## 
## [[6]]
## [1] "hello"
## 
## [[7]]
## [1] FALSE

1.4.3 Factor

Factors store the vector along with the distinct values of the elements in the vector as labels. The labels are always character irrespective of whether it is numeric or character. For example, variable gender with “male” and “female” entries:

gender <- c("male", "male", "male", " female", "female", "female")
gender <- factor(gender)

R now treats gender as a nominal (categorical) variable: 1=female, 2=male internally (alphabetically).

summary(gender)
##  female  female    male 
##       1       2       3
  • Question: why when we ran the above function i.e. summary(), it showed three and not two levels of the data? Hint: run ‘gender’.
gender
## [1] male    male    male     female female  female 
## Levels:  female female male

So, be careful of spaces!

  • Exercise: Create a gender factor with 30 male and 40 females (Hint: use the rep() function):
gender <- c(rep("male",30), rep("female", 40))
gender <- factor(gender)
gender
##  [1] male   male   male   male   male   male   male   male   male   male  
## [11] male   male   male   male   male   male   male   male   male   male  
## [21] male   male   male   male   male   male   male   male   male   male  
## [31] female female female female female female female female female female
## [41] female female female female female female female female female female
## [51] female female female female female female female female female female
## [61] female female female female female female female female female female
## Levels: female male

There are two types of categorical variables: nominal and ordinal. How to create ordered factors (when the variable is nominal and values can be ordered)? We should add two additional arguments to the factor() function: ordered = TRUE, and levels = c("level1", "level2"). For example, we have a vector that shows participants’ education level.

edu<-c(3,2,3,4,1,2,2,3,4)

education<-factor(edu, ordered = TRUE)
levels(education) <- c("Primary school","high school","College","Uni graduated")
education
## [1] College        high school    College        Uni graduated  Primary school
## [6] high school    high school    College        Uni graduated 
## Levels: Primary school < high school < College < Uni graduated
  • Exercise: We have a factor with patient and control values. Here, the first level is control and the second level is patient. Change the order of levels, so patient would be the first level:
health_status <- factor(c(rep('patient',5),rep('control',5)))
health_status
##  [1] patient patient patient patient patient control control control control
## [10] control
## Levels: control patient
health_status_reordered <- factor(health_status, levels = c('patient','control'))
health_status_reordered
##  [1] patient patient patient patient patient control control control control
## [10] control
## Levels: patient control

Finally, can you relabel both levels to uppercase characters? (Hint: check ?factor)

health_status_relabeled <- factor(health_status, levels = c('patient','control'), labels = c('Patient','Control'))
health_status_relabeled
##  [1] Patient Patient Patient Patient Patient Control Control Control Control
## [10] Control
## Levels: Patient Control

1.4.4 Matrices

All columns in a matrix must have the same mode(numeric, character, etc.) and the same length. It can be created using a vector input to the matrix function.

my_matrix = matrix(c(1,2,3,4,5,6,7,8,9), nrow = 3, ncol = 3)

my_matrix
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9

1.4.5 Data frames

Data frames can hold numeric, character or logical values. Within a column all elements have the same data type, but different columns can be of different data type. Let’s create a dataframe:

id <- 1:200
group <- c(rep("Psychotherapy", 100), rep("Medication", 100))
response <- c(rnorm(100, mean = 30, sd = 5),
             rnorm(100, mean = 25, sd = 5))

my_dataframe <-data.frame(Patient = id,
                          Treatment = group,
                          Response = response)

We also could have done the below

my_dataframe <-data.frame(Patient = c(1:200),
                          Treatment = c(rep("Psychotherapy", 100), rep("Medication", 100)),
                          Response = c(rnorm(100, mean = 30, sd = 5),
                                       rnorm(100, mean = 25, sd = 5)))

In large data sets, the function head() enables you to show the first observations of a data frames. Similarly, the function tail() prints out the last observations in your data set.

head(my_dataframe) 
tail(my_dataframe)
Patient Treatment Response
1 1 Psychotherapy 19.45338
2 2 Psychotherapy 42.34821
3 3 Psychotherapy 35.61285
4 4 Psychotherapy 25.17399
5 5 Psychotherapy 24.42101
6 6 Psychotherapy 27.48738
Patient Treatment Response
195 195 Medication 27.49956
196 196 Medication 28.42176
197 197 Medication 28.92391
198 198 Medication 29.06204
199 199 Medication 27.61078
200 200 Medication 18.95131

Similar to vectors and matrices, brackets [] are used to selects data from rows and columns in data.frames:

my_dataframe[35, 3]
## [1] 31.15347
  • Exercise: How can we get all columns, but only for the first 10 participants?
my_dataframe[1:10, ]
Patient Treatment Response
1 Psychotherapy 19.45338
2 Psychotherapy 42.34821
3 Psychotherapy 35.61285
4 Psychotherapy 25.17399
5 Psychotherapy 24.42101
6 Psychotherapy 27.48738
7 Psychotherapy 33.11080
8 Psychotherapy 42.36993
9 Psychotherapy 40.41996
10 Psychotherapy 23.41229

How to get only the Response column for all participants?

my_dataframe[ , 3]
##   [1] 19.45338 42.34821 35.61285 25.17399 24.42101 27.48738 33.11080 42.36993
##   [9] 40.41996 23.41229 28.24108 32.81045 25.70502 29.22300 32.27024 33.05726
##  [17] 24.37990 31.37091 31.16245 28.76672 38.54995 22.87417 22.20308 23.74506
##  [25] 25.12223 38.02695 28.44873 30.55478 36.07095 29.76718 37.62122 33.47683
##  [33] 32.37533 31.40624 31.15347 34.65073 22.47355 27.59239 35.01169 36.09953
##  [41] 29.29690 32.29629 31.98906 23.27326 27.44773 31.86654 17.90334 22.97421
##  [49] 30.87948 29.53606 29.08800 30.08908 21.95537 35.25648 30.40872 30.87351
##  [57] 23.57119 29.42636 32.90266 31.78335 35.53556 27.94127 28.05376 21.32260
##  [65] 25.00423 19.52317 37.05042 29.95080 21.99706 26.90433 22.47090 31.14327
##  [73] 40.54168 27.98746 28.14105 31.15181 30.93725 30.65365 23.85351 32.85104
##  [81] 33.16058 32.15789 24.70513 24.71835 29.07822 29.34360 29.97579 24.19834
##  [89] 27.12373 29.85477 27.23954 31.12225 31.72837 30.66292 28.12180 29.38940
##  [97] 35.45928 35.13103 23.55602 21.51178 23.86999 24.08943 21.19567 23.76538
## [105] 31.23768 20.12949 23.35748 25.77560 19.97935 36.95775 33.83174 27.06497
## [113] 20.82996 16.90353 20.63556 22.35301 20.31938 28.19941 31.16086 14.80019
## [121] 30.02522 28.20146 24.90908 17.37278 30.53754 25.74269 24.67774 19.84581
## [129] 15.30539 24.30986 12.59414 21.31042 18.77066 25.84767 28.36952 18.72623
## [137] 26.18819 16.69716 22.39854 23.14091 24.53845 29.56114 18.33816 30.06442
## [145] 26.48275 21.59022 19.16012 27.35389 21.63158 18.79327 20.71436 20.73652
## [153] 30.98289 29.92019 21.84922 24.98820 25.63485 28.40387 28.10618 27.49502
## [161] 22.00292 31.93532 20.70760 26.42307 30.18167 15.52820 21.29023 19.23984
## [169] 19.23642 20.05807 25.03167 31.89290 26.70301 23.68235 20.84122 17.86026
## [177] 33.32782 30.41839 25.10713 23.05902 22.40902 27.36197 24.81034 22.85248
## [185] 22.74278 20.66957 22.41073 19.05380 26.37348 24.48729 30.65553 26.59533
## [193] 30.89649 20.92523 27.49956 28.42176 28.92391 29.06204 27.61078 18.95131

Another easier way for selecting particular items is using their names that is more helpful than number of the rows in large data sets:

my_dataframe[ , "Response"]
# OR:
my_dataframe$Response

So far, we created dataframes using data.frame function from the base R. However, a better way to create dataframes is to use the tibble function from tidyverse (see here).

2 Data Cleaning

Now, suppose we ran an experiment with 141 depressed patients. Participants were randomly assigned into two treatment groups: CBT or Psychodynamic psychotherapy. We measured self-report depression scores at 5 different stages of treatment:

  • Stage 1: Before starting any treatment. It is our base stage (pre-test)
  • Stage 2: After 5 sessions of psychotherapy (post-test1)
  • Stage 3: After 10 sessions of psychotherapy (post-test2)
  • Stage 4: At the end of the treatment (post-test3)
  • Stage 5: Three months after the treatment (post-test4)

let’s read and check the uncleaned data. But, first thing first. let’s install and then load the tidyvese package. We also need some other packages:

# Install it
install.packages("tidyverse")

# And then load it
library(tidyverse)

# Load other packages that you have already installed
library(here)
library(janitor)
library(broom)
library(afex)
library(emmeans)
library(knitr)
library(kableExtra)
library(ggsci)
library(patchwork)
library(skimr)
# install.packages("devtools")
# devtools::install_github("easystats/correlation")
library("correlation")
options(scipen=999) # turn off scientific notations
options(contrasts = c('contr.sum','contr.poly')) # set the contrast sum globally 
options(knitr.kable.NA = '')
# read the raw data
raw_data <- read_csv(here("raw_data","raw_data_exp1.csv"))
head(raw_data)
progress subject response_id consent_form age gender stage1_cbt stage2_cbt stage3_cbt stage4_cbt stage5_cbt stage1_dynamic stage2_dynamic stage3_dynamic stage4_dynamic stage5_dynamic anxiety1 anxiety2 anxiety3 anxiety4 anxiety5 anxiety6 anxiety7 anxiety8 group sleep_quality life_satisfaction
100 subj1 R_1f298znjmVzcOjp I consent 18 Female 90 31 33 47 50 5 5 5 5 5 6 5 3 Psychodynamic 9 9
100 subj2 R_tL0A9P33Gi18I0N I consent 18 Male 78 46 46 11 13 6 6 6 5 6 6 5 6 CBT 9 10
100 subj3 R_1LNyJhCKxTAAMOW I consent 19 Female 68 51 24 41 24 6 5 4 5 5 6 5 6 CBT 10 8
100 subj4 R_3enxzUsEYgs5r1a I consent 27 Female 100 21 11 6 31 6 6 6 1 6 6 6 1 Psychodynamic 8 7
100 subj5 R_2Qzl2096a4KNE29 I consent 19 Male 30 28 16 6 6 6 6 5 5 6 6 6 6 CBT 11 11
100 subj6 R_esb71WOTQySjusF I consent 20 Female 79 1 57 46 57 6 6 6 5 6 6 6 6 Psychodynamic 10 10
  • Exercise: There is a dataset in the cleaned_data folder named unicef_u5mr.csv. Read the dataset using read_csv and here.
unicef_data <- read_csv(here("cleaned_data","unicef_u5mr.csv"))

In order to clean the data, we use tidyverse which is a collection of packages to work with data. One of the tidyverse packages that we use regularly is dplyr which includes several functions:

  • mutate() adds new variables or change existing ones.
  • select() pick variables (columns) based on their names.
  • filter() picks cases (rows) based on their values.
  • summarise() gives a single single summary of the data (e.g., mean, counts, etc.)
  • arrange() changes the ordering of the rows.
  • group_by() divides your dataframe into grouped dataframes and allow you to do each of the above operations (except for arrange) on every one of them separately.

2.1 Select

Pick subject, age, and gender columns:

selected_data <- select(raw_data, subject, age, gender)

2.2 Filter

Now, do the following tasks: pick all the male participants, pick all the male participants or those greater than 25 years old, and finally pick all male participants and those greater than 25 years old:

# filter all males
fil_male <- filter(raw_data, gender == "Male")
# filter males and older than 25
fil_male_and_g25 <- filter(raw_data, gender == "Male" & age > 25 )
# filter males or older than 25
fil_male_or_g25 <- filter(raw_data, gender == "Male" | age > 25 )

2.3 Arrange

Arrange (order) your dataframe based on the age, once in an ascending order (youngers first) and once based on descending order (olders first):

# order participants based on their age
arranged_data <- arrange(raw_data, age)
# order participants based on their age (descendeing)
arranged_descending <- arrange(raw_data, desc(age))

2.4 Mutate

Create a column to show if the participant has finished the task or not:

mutated_data <- mutate (raw_data, finished= case_when(progress==100~ "Yes",T~ "No"))

2.5 Summarise

Summarize participants age and sd:

summarise(raw_data, mean= mean(age, na.rm=T),
          sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655

2.6 Pipe Operators

A new function: pipe operators %>% pipes a value into the next function:

raw_data %>% 
  summarise(., mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655
raw_data %>% 
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655

Calculate the age mean of younger than 25 participants only:

raw_data %>% 
  filter (age < 25) %>%
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
19.1913 1.515393

2.7 Group By

Calculate the age mean of younger than 25 participants for each gender separately:

raw_data %>% 
  filter (age < 25) %>%
  group_by(gender) %>%
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T)) %>%
  ungroup ()
gender mean sd
Female 19.21053 1.556693
Male 19.10000 1.333772
  • Exercise: Create a column to show if participant is older than 23 or not and then calculate sleep quality (sleep_quality) mean for each group separately:
raw_data %>%
  mutate(age_group = case_when(age > 23 ~ "greater than 23", T~ "younger than 23")) %>%
  group_by(age_group) %>%
  summarise(sleep_quality = mean(sleep_quality, na.rm=T))
age_group sleep_quality
greater than 23 9.000000
younger than 23 8.107438
  • Exercise: Add the anxiety total score (sum) to the dataframe and then convert subject column to factor:
anxiety_data <- raw_data %>%
  mutate(anxiety_total= anxiety1+anxiety2+anxiety3+anxiety4+anxiety5+anxiety6+anxiety7+anxiety8) %>%
  mutate(subject= factor(subject))

2.8 Pivoting

Next, we want to pivot our data to switch between long and wide format:

# Make you data long
long_data <- raw_data %>%
  select(subject, stage1_cbt:stage5_cbt,stage1_dynamic:stage5_dynamic) %>%
  pivot_longer(cols = c(stage1_cbt:stage5_dynamic), names_to = 'stage', values_to = 'depression_score')

# Make you data wide
wide_data <- long_data %>%
  pivot_wider(names_from = stage, values_from= depression_score)
  • Exercise: Convert the UNICEF dataset to long and wide formats:
unicef_data <- read_csv(here("cleaned_data","unicef_u5mr.csv"))

library(janitor)
unicef_data_cleaned <- unicef_data %>%
  clean_names()

unicef_long_data <- unicef_data_cleaned %>% pivot_longer(cols = c(u5mr_1950:u5mr_2015), names_to = 'year', values_to = 'u5mr')
unicef_wideg_data <- unicef_long_data %>% pivot_wider(names_from = 'year', values_from = 'u5mr')

Note: The codes for the previous exercise were taken from this blog post written by Simon Ejdemyr.

Now, let’s do some cleaning using dplyr, tidyr and other tidyverse libraries:

cleaned_data <- raw_data %>% 
  filter(progress == 100) %>% # filter out unfinished participants
  select(-consent_form) %>% #remove some useless columns
  # create a total score for our questionnaire
  mutate(anxiety_total= anxiety1+anxiety2+anxiety3+anxiety4+anxiety5+anxiety6+anxiety7+anxiety8) %>%
  select(-anxiety1:-anxiety8) %>%
  # make our dataframe long
  pivot_longer(cols = c(stage1_cbt:stage5_cbt,stage1_dynamic:stage5_dynamic),names_to = 'stage',values_to = 'depression_score') %>% 
  #pivot_wider(names_from = stage, values_from= depression_score) # this code change our dataframe back to wide
  filter(!is.na(depression_score)) %>% #remove rows with depression_score == NA
  mutate(stage= gsub("_.*", "", stage)) %>%
  select (subject, age, gender, group, stage, depression_score, anxiety_total, sleep_quality, life_satisfaction)
subject age gender group stage depression_score anxiety_total sleep_quality life_satisfaction
subj1 18 Female Psychodynamic stage1 90 39 9 9
subj1 18 Female Psychodynamic stage2 31 39 9 9
subj1 18 Female Psychodynamic stage3 33 39 9 9
subj1 18 Female Psychodynamic stage4 47 39 9 9
subj1 18 Female Psychodynamic stage5 50 39 9 9
subj2 18 Male CBT stage1 78 46 9 10

Ok, now the data is clean and tidy which means:

  1. Each variable forms a column.
  2. Each observation forms a row.
  3. Each type of observational unit forms a table (Wickham, 2014).

Check the dataframe and all the data types:

str(cleaned_data)
## tibble [655 × 9] (S3: tbl_df/tbl/data.frame)
##  $ subject          : chr [1:655] "subj1" "subj1" "subj1" "subj1" ...
##  $ age              : num [1:655] 18 18 18 18 18 18 18 18 18 18 ...
##  $ gender           : chr [1:655] "Female" "Female" "Female" "Female" ...
##  $ group            : chr [1:655] "Psychodynamic" "Psychodynamic" "Psychodynamic" "Psychodynamic" ...
##  $ stage            : chr [1:655] "stage1" "stage2" "stage3" "stage4" ...
##  $ depression_score : num [1:655] 90 31 33 47 50 78 46 46 11 13 ...
##  $ anxiety_total    : num [1:655] 39 39 39 39 39 46 46 46 46 46 ...
##  $ sleep_quality    : num [1:655] 9 9 9 9 9 9 9 9 9 9 ...
##  $ life_satisfaction: num [1:655] 9 9 9 9 9 10 10 10 10 10 ...

Finally, we save our data to the cleaned_data folder.

write_csv(cleaned_data, here("cleaned_data","cleaned_data_exp1.csv"))

3 Data Visualization

Before starting the ggplot, let’s try a visualization using a function from the Base R the plot() function shows the association of each variable against the other one in a data handy for data with few number of variables to see if there are any patterns

exam_data<- read_csv(here::here("cleaned_data", "exam_data.csv"))

plot(x = exam_data$Anxiety, y = exam_data$Exam)

The code also works without writing x and y, however, writing them is strongly recommended

plot(exam_data$Anxiety, exam_data$Exam)

ggplot, the gg in ggplot stands for grammar of graphics. Grammar of graphics basically says any graphical representation of data, can be produced by a series of layers. You can think of a layer as a plastic transparency. Lets draw the same plot using ggplot. Always, mention the data we are going to work with.

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))

  • aes: aes which stands for aesthetics is a relationship between a variable in your dataset and an aspect of the plot that is going to visually convey the information to the reader

  • Visual elements are known as geoms (short for ‘geometric objects’) in ggplot 2. When we define a layer, we have to tell R what geom we want displayed on that layer (do we want a bar, line dot, etc.?)

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))+ geom_point()

So, lets try some of them here like shape and size. Be careful with the + sign, if you clink enter for the next part of the code, the + sign should not go to the next line

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))+
  geom_point(size = 2, shape = 8)

The current plot is not very informative about the patterns for each gender.

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender))+
  geom_point(size = 2, shape = 10)

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender, shape = Gender))+
  geom_point(size = 2, shape = 10)

Question: why the above code doesn’t make any change?

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender, shape = Gender))+
  geom_point(size = 2)

Can assign the first layer to a variable to reduce the length of codes for next layers.

My_graph <- ggplot(data = exam_data, aes(x = Exam, y = Anxiety))

My_graph + geom_point()

lets add a line to the current graph

My_graph + geom_point() + geom_smooth()

Aesthetics can be set for all layers of the plot (i.e., defined in the plot as a whole) or can be set individually for each geom in a plot.

My_graph + geom_point(aes(color = Gender)) + geom_smooth()

My_graph + geom_point(aes(color = Gender)) + geom_smooth(aes(color = Gender))

The shaded area around the line is the 95% confidence interval around the line. We can switch this off by adding se = F (which is short for ‘standard error = False’)

My_graph + geom_point() + geom_smooth(se = F)

What if we want our line to be a direct line?

My_graph + geom_point() + geom_smooth(se = F, method = lm)

How to change the labels of x and y axes?

My_graph + geom_point() + geom_smooth(se = F, method = lm) +
  labs(x = "Exam scores %", y = "Anxiety scores")

Histograms are used to show distributions of variables while bar charts are used to compare variables. Histograms plot quantitative data with ranges of the data grouped into bins or intervals while bar charts plot categorical data.

#ggplot(data = exam_data, aes(x = Anxiety, y = Exam )) + geom_histogram()
# the code above gives an error as geom_histogram can only have x or y axis in its aes()

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram()

ggplot(data = exam_data, aes(y = Anxiety)) + geom_histogram()

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31)

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31, fill = "green")

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31, fill = "green", col = "red")

Let’s stop using the My_graph variable and write the whole code from the start again for a bar chart

ggplot(data = exam_data, aes(x = Sleep_quality))+
  geom_bar()

Because we want to plot a summary of the data (the mean) rather than the raw scores themselves, we have to use a stat.

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  geom_bar(stat = "summary", fun = "mean")

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  geom_bar(stat = "summary", fun = "mean", position = "dodge")

The other way to get the same plot that the code above gives, is using the stat_summary function that takes the following general form: stat_summary(function = x, geom = y)

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  stat_summary(fun = mean, geom = "bar", position = "dodge")

How to combine multiple plots? How to combine multiple plots? We can use the patchwork package. A nice tutorial on using this package can be found here

p1 = My_graph + geom_point(aes(color = Gender)) + geom_smooth()

p2 = ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31)

p3 = ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  stat_summary(fun = mean, geom = "bar", position = "dodge")

p4 = My_graph + geom_point() + geom_smooth(se = F, method = lm) +
  labs(x = "Exam scores %", y = "Anxiety scores")

combined = p1 + p2+ p3 + p4 + plot_layout(nrow = 4, byrow = F)

combined

p1 | p2 / p3 / p4

p1 | p2 / (p3 / p4)

ggsave() function, which is a versatile exporting function that can export as PostScript (.eps/.ps), tex (pictex), pdf, jpeg, tiff, png, bmp, svg and wmf (in Windows only). In its basic form, the structure of the function is very simple: ggsave(filename)

ggsave(combined, filename = here("outputs", "combined.png"), dpi=300)

Now that we learned the basics of ggplot, let’s draw some plot for our experiment data. First, we need to create a dataset with aggregated depression_score scores over group and stage. We will use this dataset for line and bar graphs.

library(ggsci)

data_exp1_orig <- read_csv(here("cleaned_data","cleaned_data_exp1.csv"))

data_exp1 <- data_exp1_orig%>% 
  #mutate_if(is.character, factor) %>%
  mutate(subject= factor(subject), # convert all characters to factor
         group = factor(group),
         stage = factor(stage))


aggregated_data_exp1 <- data_exp1 %>%
  group_by(stage, group) %>%
  mutate(depression_score = mean(depression_score)) %>%
  ungroup()


barplot_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=stage, y= depression_score, fill=group)) +
  geom_bar(stat = "identity", position= "dodge")+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  scale_fill_jama() 

#ggsave(barplot_exp1, filename = here("outputs","barplot_exp1.png"), dpi=300)


barplot_facet_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=group, y= depression_score, fill=stage)) +
  geom_bar(stat = "identity", position= "dodge")+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "none",
        axis.text=element_text(size=11),
        axis.title = element_text(size = 12)) +
  facet_wrap(~stage, nrow = 1)+
  scale_fill_jco() 

#ggsave(barplot_facet_exp1, filename = here("outputs","barplot_facet_exp1.png"), dpi=300)


lineplot_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, group= group, color= group)) +
  geom_line(aes(linetype= group)) +
  geom_point(size= 5)+
  labs (x= '', y= "Depression Score") + 
  theme_classic() +
  theme(legend.position = "bottom",
        axis.text=element_text(size=11),
        axis.title = element_text(size = 12)) +
  scale_color_nejm() 

#ggsave(lineplot_exp1, filename = here("outputs","lineplot_exp1.png"), dpi=300)


violinplot_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_violin()+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11)) +
  scale_fill_d3() 

#ggsave(violinplot_exp1, filename = here("outputs","violinplot_exp1.png"), dpi=300)


boxplot_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_boxplot()+
  #geom_point(position = position_dodge(width=0.75), alpha= .5)+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11)) +
  scale_fill_simpsons() 

#ggsave(boxplot_exp1, filename = here("outputs","boxplot_exp1.png"), dpi=300)


boxplot_facet_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_boxplot()+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  facet_wrap(~group)+
  scale_color_simpsons() 

#ggsave(boxplot_facet_exp1, filename = here("outputs","boxplot_facet_exp1.png"), dpi=300)

Let’s combine our plots:

combined_plot_exp1 <- barplot_facet_exp1 / (lineplot_exp1+violinplot_exp1+boxplot_exp1)
combined_plot_exp1

And here, we save our plots to the outputs folder.

ggsave(combined_plot_exp1, filename = here("outputs","combined_plot_exp1.png"), dpi=300, width = 12)

4 Descriptive Statistics

Now, let’s do some descriptive statistics. Now, we can open a new script called data_analysis.r and read some datasets. Then we use skimr package to describe our data.

narcissism_data <- read_csv(here("cleaned_data","narcissism_data.csv"))
narcissism_data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 131
Number of columns 5
_______________________
Column type frequency:
character 1
numeric 4
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
subject 0 1 5 7 0 131 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
psychopathy 0 1 8.78 2.27 0 8.0 10 10 11 ▁▁▁▂▇
self_esteem 0 1 8.45 1.68 4 8.0 8 9 12 ▁▅▇▆▃
narcissism 0 1 38.20 6.15 19 33.5 39 43 48 ▁▂▇▇▆
mental_health 0 1 3.19 1.04 1 3.0 4 4 4 ▂▂▁▃▇
  • Exercise: Open the dataset called treatment_data.csv and do a descriptive analysis:
treatment_data <- read_csv(here("cleaned_data","treatment_data.csv"))
treatment_data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 131
Number of columns 7
_______________________
Column type frequency:
character 3
numeric 4
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
subject 0 1 5 7 0 131 0
gender 0 1 4 6 0 2 0
treatment 0 1 3 13 0 2 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
anxiety 0 1 62.35 24.51 0 40.0 69 81.0 100 ▂▆▃▇▆
depression 0 1 52.50 22.12 0 34.5 51 71.0 100 ▂▇▇▆▃
life_satisfaction 0 1 41.02 23.93 0 21.0 39 56.5 100 ▅▇▅▃▂
  • Exercise: Do the same thing for the memory_data.csv.
memory_data <- read_csv(here("cleaned_data","memory_data.csv"))
memory_data %>% group_by(time) %>%
  skimr::skim()
Data summary
Name Piped data
Number of rows 262
Number of columns 5
_______________________
Column type frequency:
character 2
numeric 2
________________________
Group variables time

Variable type: character

skim_variable time n_missing complete_rate min max empty n_unique whitespace
subject post_test_memory 0 1 5 7 0 131 0
subject pre_test_memory 0 1 5 7 0 131 0
gender post_test_memory 0 1 4 6 0 2 0
gender pre_test_memory 0 1 4 6 0 2 0

Variable type: numeric

skim_variable time n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age post_test_memory 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
age pre_test_memory 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
memory_score post_test_memory 0 1 52.50 22.12 0 34.5 51 71.0 100 ▂▇▇▆▃
memory_score pre_test_memory 0 1 41.02 23.93 0 21.0 39 56.5 100 ▅▇▅▃▂

Now, let’s describe our experiment data:

data_exp1_orig <- read_csv(here("cleaned_data","cleaned_data_exp1.csv"))

How many participants in total?

data_exp1 %>% summarise(n= n_distinct(subject))
n
131

How many participants do we have in each group?

data_exp1 %>% 
  group_by(subject) %>% 
  filter(row_number()==1) %>% 
  ungroup () %>% 
  group_by(group) %>% 
  count() 
group n
CBT 66
Psychodynamic 65

Find the mean and sd for numeric variables using base R summary function:

data_exp1 %>% 
  group_by(subject) %>% 
  filter(row_number()==1) %>% 
  ungroup () %>%
  summary()
##     subject         age           gender                    group   
##  subj1  :  1   Min.   :16.00   Length:131         CBT          :66  
##  subj10 :  1   1st Qu.:18.00   Class :character   Psychodynamic:65  
##  subj101:  1   Median :19.00   Mode  :character                     
##  subj102:  1   Mean   :21.15                                        
##  subj103:  1   3rd Qu.:20.00                                        
##  subj104:  1   Max.   :63.00                                        
##  (Other):125                                                        
##     stage     depression_score anxiety_total  sleep_quality   
##  stage1:131   Min.   :  1.00   Min.   :19.0   Min.   : 0.000  
##  stage2:  0   1st Qu.: 29.00   1st Qu.:33.5   1st Qu.: 8.000  
##  stage3:  0   Median : 51.00   Median :39.0   Median :10.000  
##  stage4:  0   Mean   : 53.33   Mean   :38.2   Mean   : 8.779  
##  stage5:  0   3rd Qu.: 79.00   3rd Qu.:43.0   3rd Qu.:10.000  
##               Max.   :101.00   Max.   :48.0   Max.   :11.000  
##                                                               
##  life_satisfaction
##  Min.   : 4.00    
##  1st Qu.: 8.00    
##  Median : 8.00    
##  Mean   : 8.45    
##  3rd Qu.: 9.00    
##  Max.   :12.00    
## 

Alternatively, we can use skimr library:

data_exp1 %>% 
  group_by(subject) %>% 
  filter(row_number()==1) %>% 
  ungroup () %>% 
  dplyr::select (age, depression_score, anxiety_total, sleep_quality, life_satisfaction) %>% 
  skimr::skim()
skim_type skim_variable n_missing complete_rate numeric.mean numeric.sd numeric.p0 numeric.p25 numeric.p50 numeric.p75 numeric.p100 numeric.hist
numeric age 0 1 21.152672 6.515630 16 18.0 19 20 63 ▇▁▁▁▁
numeric depression_score 0 1 53.328244 27.967685 1 29.0 51 79 101 ▅▇▅▇▅
numeric anxiety_total 0 1 38.198473 6.153698 19 33.5 39 43 48 ▁▂▇▇▆
numeric sleep_quality 0 1 8.778626 2.274576 0 8.0 10 10 11 ▁▁▁▂▇
numeric life_satisfaction 0 1 8.450382 1.683466 4 8.0 8 9 12 ▁▅▇▆▃
  • Exercise: For this exercise, we use a dataset of one of my own studies. In this study, we asked participants to guess the physical brightness of reasoning arguments and then we gave a cognitive ability test. (See the original study here). Open ghasemi_brightness_exp4.csv file and answer to the following questions:
  1. How many participants did we test in total?
  2. Find out how many male and female we tested.
  3. Calculate mean and sd for age and cognitive ability (cog_ability).
ghasemi_data <- read_csv(here("cleaned_data","ghasemi_brightness_exp4.csv"))

ghasemi_data %>% summarise(n = n_distinct(participant)) # number of participants:200
n
200
ghasemi_data %>% group_by (participant) %>% filter (row_number()==1) %>% group_by (gender) %>% summarise(n= n()) %>% ungroup() # 183 female, 17 male
gender n
Female 183
Male 17
ghasemi_data %>% dplyr::select (age, cog_ability) %>% skimr::skim() # mean and sd for age and cognitive ability
Data summary
Name Piped data
Number of rows 38400
Number of columns 2
_______________________
Column type frequency:
numeric 2
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age 0 1 22.20 6.78 17 19 20 22 52 ▇▁▁▁▁
cog_ability 0 1 39.55 9.46 11 34 40 46 61 ▁▃▇▆▂

5 Data Analysis

5.1 t-test

Now, we use the treatment data to run three different independent t-tests. Suppose we did an experiment to compare the effectiveness of CBT vs. Psychodynamic therapies in decreasing anxiety, and depression and also in improving life satisfaction:

# t.test (indep)
t.test(anxiety~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  anxiety by treatment
## t = -0.85021, df = 124.18, p-value = 0.3968
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -12.11096   4.83264
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    60.54545                    64.18462
t.test(depression~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  depression by treatment
## t = -2.8725, df = 123.97, p-value = 0.004792
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -18.21965  -3.35424
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    47.15152                    57.93846
t.test(life_satisfaction~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  life_satisfaction by treatment
## t = -5.2688, df = 127.11, p-value = 0.0000005699
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -27.61850 -12.53721
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    31.06061                    51.13846

In another experiment, suppose we have created a method to boost memory. Then, we recruit some participants, do a memory pre-test, implement the method, and do a memory post-test, Now, we want to see whether our method have improved participants’ memory:

# t.test (paired)
t.test(memory_score~time, data= memory_data, paired= T)
## 
##  Paired t-test
## 
## data:  memory_score by time
## t = 5.4761, df = 130, p-value = 0.0000002163
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##   7.333171 15.628661
## sample estimates:
## mean of the differences 
##                11.48092

Now that we learned about t-test, let’s perform this test on our dataset. Is there a difference between groups at the first stage? Ideally, we want participants’ depresion score at the first stage to be similar for both groups because we have not started our treatment yet. Previous graphs showed us that depression scores of the CBT and Psychodynamic groups at this stage are pretty close. Let’s test that using an independent t-test (because we have 2 independent groups):

# Is there a difference between groups at the first stage?
data_exp1 %>% 
  group_by(group) %>% 
  filter(stage=='stage1') %>% 
  ungroup () %>%
  t.test(depression_score~group, data = ., paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  depression_score by group
## t = 0.10768, df = 118.92, p-value = 0.9144
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -9.205588 10.264329
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    53.59091                    53.06154

Now, we wonder if psychotherapy treatments were effective at all, regardless of the treatment method. So, we would like to test if depresion score at the forth stage are lower than scores at the stage 2? Since a pair of score at stage 2 and stage 4 is coming from a same person, we use paired t-test.

# Is there a difference between ratings of stage2 and stage4?
data_exp1 %>% 
  filter(stage=='stage2' | stage=='stage4') %>% 
  ungroup () %>%
  t.test(depression_score~stage, data = ., paired=TRUE)
## 
##  Paired t-test
## 
## data:  depression_score by stage
## t = 5.5931, df = 130, p-value = 0.0000001261
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##   7.70108 16.13098
## sample estimates:
## mean of the differences 
##                11.91603
  • Exercise: John et al. (2019) investigated the consequences of backing down (changing one’s mind in lights of evidence)and how other people view someone who change their mind. In their second experiments, they presented participants either with a person who changes their mind or a person who refuses to back down. Then, they asked participants to rate how intelligent and confident the person is (See the original study here). They reported that:

“Relative to the entrepreneur who did not back down, participants judged the entrepreneur who backed down as more intelligent (M_backed_down=5.13 out of 7, SD=1.09; M_did_not_back_down=3.97, SD=1.54; t(271.12)=−7.59, p < .001) but less confident (M_backed_down=4.50 out of 7, SD=1.36; M_did_not_back_down=5.65, SD=1.10; t(291.01)=8.08, p < .001).”.

Open the john_backdown_exp2.csv file and try to reproduce their results. Run two separate independent t-test, one with intelligent as the dependent variable and one with confident as the dependent variable. For both t-test, use back_down as the between-subject independent variable.

john_data <- read_csv(here("cleaned_data","john_backdown_exp2.csv"))


t.test(intelligent~back_down, data = john_data, paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  intelligent by back_down
## t = 7.5853, df = 271.12, p-value = 0.0000000000005319
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.8577107 1.4590076
## sample estimates:
##       mean in group backed_down mean in group did_not_back_down 
##                        5.129412                        3.971053
t.test(confident~back_down, data = john_data, paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  confident by back_down
## t = -8.0763, df = 291.01, p-value = 0.00000000000001787
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -1.4257768 -0.8670294
## sample estimates:
##       mean in group backed_down mean in group did_not_back_down 
##                        4.503268                        5.649671

5.2 Analysis of Variance (ANOVA)

Now, let’s analysis our main experiment data: Do participants in the CBT group show better outcome compared to participants in the Psychodynamic group? Suppose we believe that participants should show lower depression after 5 or 10 sessions of both psychotherapy treatments and this decrease should be more pronounced for CBT than psychodynamic psychotherapy. If this is the case. we expect an interaction in the traditional Analysis of Variance (AONVA) test.

aov_m1 <- aov_car (depression_score ~ group*stage +
                     Error(subject/stage), data = data_exp1)  
Effect df MSE F ges p.value
group 1, 129 737.60 27.08 *** .066 <.001
stage 2.97, 382.72 492.81 53.15 *** .215 <.001
group:stage 2.97, 382.72 492.81 8.91 *** .044 <.001

As you can see, we found a significant main effect of stage and a significant group by stage interaction. We can use the emmeans package to do post-hoc tests.

# main effect of stage
emmeans(aov_m1, 'stage')
##  stage  emmean   SE  df lower.CL upper.CL
##  stage1   53.3 1.83 579     49.7     56.9
##  stage2   33.3 1.83 579     29.7     36.9
##  stage3   26.3 1.83 579     22.7     29.9
##  stage4   21.4 1.83 579     17.8     25.0
##  stage5   31.4 1.83 579     27.8     35.0
## 
## Results are averaged over the levels of: group 
## Warning: EMMs are biased unless design is perfectly balanced 
## Confidence level used: 0.95
pairs(emmeans(aov_m1, 'stage'), adjust= 'holm')
##  contrast        estimate   SE  df t.ratio p.value
##  stage1 - stage2    20.03 2.36 516  8.480  <.0001 
##  stage1 - stage3    26.94 2.36 516 11.404  <.0001 
##  stage1 - stage4    31.91 2.36 516 13.506  <.0001 
##  stage1 - stage5    21.84 2.36 516  9.245  <.0001 
##  stage2 - stage3     6.91 2.36 516  2.924  0.0144 
##  stage2 - stage4    11.87 2.36 516  5.027  <.0001 
##  stage2 - stage5     1.81 2.36 516  0.766  0.4442 
##  stage3 - stage4     4.97 2.36 516  2.102  0.0941 
##  stage3 - stage5    -5.10 2.36 516 -2.158  0.0941 
##  stage4 - stage5   -10.07 2.36 516 -4.261  0.0001 
## 
## Results are averaged over the levels of: group 
## P value adjustment: holm method for 10 tests
# group by stage interaction
emmeans(aov_m1, "group", by= "stage")
## stage = stage1:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             53.5 2.59 577    48.47     58.6
##  Psychodynamic   53.0 2.60 580    47.92     58.1
## 
## stage = stage2:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             30.7 2.59 577    25.58     35.7
##  Psychodynamic   35.9 2.60 580    30.75     41.0
## 
## stage = stage3:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             21.7 2.59 577    16.62     26.8
##  Psychodynamic   31.0 2.60 580    25.89     36.1
## 
## stage = stage4:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             13.4 2.59 577     8.29     18.4
##  Psychodynamic   29.4 2.60 580    24.29     34.5
## 
## stage = stage5:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             18.8 2.59 577    13.74     23.9
##  Psychodynamic   44.1 2.60 580    38.96     49.2
## 
## Warning: EMMs are biased unless design is perfectly balanced 
## Confidence level used: 0.95
update(pairs(emmeans(aov_m1, "group", by= "stage")), by = NULL, adjust = "holm") 
##  contrast            stage  estimate   SE  df t.ratio p.value
##  CBT - Psychodynamic stage1    0.529 3.67 579  0.144  0.8852 
##  CBT - Psychodynamic stage2   -5.195 3.67 579 -1.417  0.3138 
##  CBT - Psychodynamic stage3   -9.288 3.67 579 -2.534  0.0346 
##  CBT - Psychodynamic stage4  -16.022 3.67 579 -4.371  0.0001 
##  CBT - Psychodynamic stage5  -25.244 3.67 579 -6.887  <.0001 
## 
## P value adjustment: holm method for 5 tests

You can use the afex_plot function from afex to create beautiful plots. Those plots interacts nicely with ggplot:

afex_plot(aov_m1, x = "stage", trace = "group", error='between',
          line_arg = list(size=1),
          point_arg = list(size=3.5),
          data_arg = list(size= 1, color= 'grey', width=.4),
          data_geom = geom_boxplot,
          mapping = c("linetype", "shape", "fill"),
          legend_title = "Group") +
  labs(y = "Depression Score", x = "") +
  theme_bw()+ # remove the grey background and grid
  theme(axis.text=element_text(size=13),
        axis.title = element_text(size = 13),
        legend.text=element_text(size=13),
        legend.title=element_text(size=13),
        legend.position='bottom',
        legend.key.size = unit(1, "cm"),
        legend.background = element_rect(colour = 'black', fill = 'white', linetype='solid'))+
  scale_color_simpsons() +
  scale_fill_simpsons()

If you are interested in this topic, check out this nice tutorial about using afex to run ANOVA, and also this interesting tutorial on the emmeans package.

  • Exercise: Rotello et al. (2018) investigated the association between the race (White vs. Black faces) and the gun-tool judgments. In their first experiments, they presented participants with 16 White male faces and 16 Black male faces, and following that 8 images of guns and 8 images of tools. They asked participants to judge if the object is a tool or a gun by pressing keyboard buttons. Then, they ran an ANOVA to see if participants’ gun responses are higher for any of the races. So, they included prime race (Black, White) and target identity (gun, tool) as independent variables and participants’ gun responses as dependent variable into their linear model (See the original study here). They found that:

“Participants made more gun responses to guns than to tools, F(1,45) = 53243, p < 0.0001, η2g = 0.998. However, the race of the prime face did not matter, F(1,45) = 0.287, p > 0.59, η2g = 0.001, nor was there an interaction of prime race with target object, F(1,45) = 0.022, p > 0.88, η2g = 0.000)”.

Open the rotello_shooter_exp1.csv file and try to reproduce their results. Run an ANOVA (type III) with resp as the dependent variable and target, prime, and their interaction as independent variables.

# load the general data file
rotello_data <- read_csv(here("cleaned_data","rotello_shooter_exp1.csv"))

# ANOVA
rotello_aov <- aov_car (resp ~ target*prime +
           Error(subject/target*prime), data = rotello_data)
Effect df MSE F ges p.value
target 1, 45 0.00 53242.99 *** .998 <.001
prime 1, 45 0.00 0.29 .001 .595
target:prime 1, 45 0.00 0.02 <.001 .883

5.3 Correlation

Here, we want to check the correlation between variables on the narcissism_data. First, we need to remove subject column because it is not numeric:

narcissism_data_cor <- narcissism_data %>%
  select(-subject)
#-- Base R:
cor(narcissism_data_cor, method = "pearson",  use = "complete.obs")

#-- Psych library:
psych::pairs.panels(narcissism_data_cor, method = "pearson", hist.col = "#00AFBB", density = T, ellipses = F, stars = T)

#-- Correlation library:
# install.packages("devtools")
# devtools::install_github("easystats/correlation")
#library("correlation")
correlation::correlation(narcissism_data_cor) %>% summary()

#-- apaTables library:
narcissism_data_cor %>% 
  apaTables::apa.cor.table(filename="./outputs/CorMatrix.doc", show.conf.interval=T)
psychopathy self_esteem narcissism mental_health
psychopathy 1.00 0.15 0.40 -0.44
self_esteem 0.15 1.00 0.11 -0.29
narcissism 0.40 0.11 1.00 -0.26
mental_health -0.44 -0.29 -0.26 1.00
Parameter mental_health narcissism self_esteem
psychopathy -0.44 0.40 0.15
self_esteem -0.29 0.11
narcissism -0.26
  • Exercise: Pennycook et al. (2020) investigated the relationship between actively open-minded thinking style about evidence (AOT-E) and different political, scientific, and religious beliefs (see the original paper here). In their first experiment, they calculated the correlation of AOTE and scientific beliefs items (global warming, evolution, etc.) and they found the following results:

Open the pennycook_aote_exp1.csv file and try to reproduce their results by creating the same correlation matrix.

pennycook_data <- read_csv(here("cleaned_data","pennycook_aote_exp1.csv")) 


#---------- Base R:
cor(pennycook_data, method = "pearson",  use = "complete.obs")

#---------- Psych library:
pennycook_data %>% 
  psych::pairs.panels(method = "pearson", hist.col = "#00AFBB", density = T, ellipses = F, stars = T)

#---------- Correlation library:
correlation::correlation(pennycook_data) %>% summary()

#---------- apaTables library:
pennycook_data %>% 
  apaTables::apa.cor.table(filename="./outputs/CorMatrix.doc", show.conf.interval=T)
Parameter trust_scien gm_health tech_problems modern_medicine old_earth vaccines stem_cell big_bang evolution global_warming
aote 0.35 0.36 0.44 0.33 0.40 0.47 0.45 0.51 0.51 0.37
global_warming 0.42 0.06 0.14 0.18 0.33 0.26 0.31 0.33 0.38
evolution 0.48 0.33 0.28 0.36 0.47 0.39 0.54 0.78
big_bang 0.49 0.37 0.28 0.36 0.45 0.37 0.54
stem_cell 0.47 0.34 0.36 0.47 0.40 0.40
vaccines 0.43 0.52 0.49 0.53 0.38
old_earth 0.29 0.24 0.21 0.33
modern_medicine 0.43 0.42 0.47
tech_problems 0.33 0.39
gm_health 0.31

5.4 Linear Regression

Here, we do single and multiple linear regreassion on the narcissism_data:

m1 <- lm(mental_health~narcissism, data= narcissism_data)
term estimate std.error statistic p.value
(Intercept) 4.86 0.56 8.75 0
narcissism -0.04 0.01 -3.04 0
m2 <- lm(mental_health~narcissism+psychopathy, data= narcissism_data)
term estimate std.error statistic p.value
(Intercept) 5.43 0.53 10.27 0.00
narcissism -0.02 0.01 -1.09 0.28
psychopathy -0.19 0.04 -4.71 0.00
  • Exercise: Trémolière and Djeriouat (2020) examined the role of cognitive reflection and belief in science in climate change skepticism. In their first study, they revealed that cognitive reflection and belief in science negetively predicted climate change skepticism even after controlling for demographic and cognitive ability variables (see the original paper here).

Open the tremoliere_data_exp1.csv file and try to reproduce their results by running a multiple linear regression. Enter age, gender, education, belief in science, literacy, numeracy (Numtotal), and cognitive reflection as predictors and enter climate change skepticism (climato) as the outcome variable.

Tremoliere_data <- read_csv(here("cleaned_data","tremoliere_data_exp1.csv"))

Tremoliere_reg=lm(Climato ~ Age+ Gender+ Education+ BeliefInSciencetotal+ Literacy+ Numtotal+ CognitiveReflection,
                    data=Tremoliere_data)
term estimate std.error statistic p.value
(Intercept) 57.57 5.19 11.09 0.00
Age 0.01 0.05 0.24 0.81
Gender -5.68 1.34 -4.23 0.00
Education 0.54 0.38 1.43 0.15
BeliefInSciencetotal -0.20 0.06 -3.62 0.00
Literacy -0.49 0.51 -0.96 0.34
Numtotal -1.52 0.83 -1.82 0.07
CognitiveReflection -18.58 4.26 -4.37 0.00
r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC deviance df.residual nobs
0.19 0.17 12.65 11.91 0 7 -1467.77 2953.54 2988.81 58235.89 364 372

6 Rmarkdown

To be completed…

7 References

  • Ghasemi, O., Handley, S., & Howarth, S. (2020). The Bright Homunculus in our Head: Individual Differences in Intuitive Sensitivity to Logical Validity.

  • John, L. K., Jeong, M., Gino, F., & Huang, L. (2019). The self-presentational consequences of upholding one’s stance in spite of the evidence. Organizational Behavior and Human Decision Processes, 154, 1-14.

  • Pennycook, G., Cheyne, J. A., Koehler, D. J., & Fugelsang, J. A. (2020). On the belief that beliefs should change according to evidence: Implications for conspiratorial, moral, paranormal, political, religious, and science beliefs. Judgment and Decision Making, 15(4), 476.

  • Rotello, C. M., Kelly, L. J., Heit, E., Vazire, S., & Vul, E. (2018). The Shape of ROC Curves in Shooter Tasks: Implications for Best Practices in Analysis. Collabra: Psychology, 4(1).

  • Trémolière, B., & Djeriouat, H. (2020). Don’t you see that its cold! Exploring the roles of cognitive reflection, climate science literacy, illusion of knowledge, and political orientation in climate change skepticism.

  • Wickham, H. (2014). Tidy data. Journal of Statistical Software, 59(10), 1-23.

LS0tCnRpdGxlOiAiUiBmb3IgRGF0YSBBbmFseXNpcyIKYXV0aG9yOgogIC0gbmFtZTogIk9taWQgR2hhc2VtaSIKICAgIGFmZmlsaWF0aW9uOiBNYWNxdWFyaWUgVW5pdmVyc2l0eQogICAgZW1haWw6IG9taWRyZXphLmdoYXNlbWlAaGRyLm1xLmVkdS5hdQogIC0gbmFtZTogIk1haGRpIE1hemlkaSIKICAgIGFmZmlsaWF0aW9uOiBVbml2ZXJzaXR5IG9mIFdlc3Rlcm4gQXVzdHJhbGlhCiAgICBlbWFpbDogbWFoZGkubWF6aWRpc2hhcmFmYWJhZGlAcmVzZWFyY2gudXdhLmVkdS5hdQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGtlZXBfbWQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogY2VydWxlYW4KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICNjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogImthYmxlIgotLS0KClRoaXMgZG9jdW1lbnQgaXMgdGhlIHN1bW1hcnkgb2YgdGhlICoqUiBmb3IgRGF0YSBBbmFseXNpcyoqIHdvcmtzaG9wLiAKCkFsbCBjb3JyZXNwb25kZW5jZSByZWxhdGVkIHRvIHRoaXMgZG9jdW1lbnQgc2hvdWxkIGJlIGFkZHJlc3NlZCB0bzogCgo8Y2VudGVyPgpPbWlkIEdoYXNlbWkgKE1hY3F1YXJpZSBVbml2ZXJzaXR5LCBTeWRuZXksIE5TVywgMjEwOSwgQVVTVFJBTElBKSAKCkVtYWlsOiBvbWlkcmV6YS5naGFzZW1pQGhkci5tcS5lZHUuYXUgCjwvY2VudGVyPgoKCgo8c3R5bGU+Cgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIHRleHQtYWxpZ246IGp1c3RpZnk7CiAgICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwp9CmNvZGUucnsgLyogQ29kZSBibG9jayAqLwogICAgZm9udC1zaXplOiAxNHB4Owp9CnByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovCiAgICBmb250LXNpemU6IDEycHg7Cn0KCjwvc3R5bGU+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbj0iY2VudGVyIikKYGBgCgoKCmBgYHtyIGxpYnJhcmllcywgbWVzc2FnZT1GQUxTRSwgZWNobz1GfQojIGxvYWQgbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShicm9vbSkKbGlicmFyeShhZmV4KQpsaWJyYXJ5KGVtbWVhbnMpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZ3NjaSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoc2tpbXIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZWFzeXN0YXRzL2NvcnJlbGF0aW9uIikKbGlicmFyeSgiY29ycmVsYXRpb24iKQpvcHRpb25zKHNjaXBlbj05OTkpICMgdHVybiBvZmYgc2NpZW50aWZpYyBub3RhdGlvbnMKb3B0aW9ucyhjb250cmFzdHMgPSBjKCdjb250ci5zdW0nLCdjb250ci5wb2x5JykpICMgc2V0IHRoZSBjb250cmFzdCBzdW0gZ2xvYmFsbHkgCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKYGBgCgoKIyBJbnRyb2R1Y3Rpb24gdG8gUgoKIyMgQmFzaWNzIGFuZCBWYXJpYWJsZXMKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSI3MDBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3JfZmlyc3RfdGhlbi5wbmcnKSkKYGBgCgoKUiBjYW4gYmUgdXNlZCBhcyBhIGNhbGN1bGF0b3IuIEZvciBtYXRoZW1hdGljYWwgcHVycG9zZXMsIGJlIGNhcmVmdWwgb2YgdGhlIG9yZGVyIGluIHdoaWNoIFIgZXhlY3V0ZXMgdGhlIGNvbW1hbmRzLgoKYGBge3J9CjEwICsgMTAKCjQgXiAyCgooMjUwIC8gNTAwKSAqIDEwMApgYGAKClIgaXMgYSBiaXQgZmxleGlibGUgd2l0aCBzcGFjaW5nIChidXQgbm8gc3BhY2luZyBpbiB0aGUgbmFtZSBvZiB2YXJpYWJsZXMgYW5kIHdvcmRzKQoKYGBge3J9CjEwKzEwCgoxMCAgICAgICAgICAgICAgICAgKyAgICAgICAgICAgMTAKYGBgCgpSIGNhbiBzb21ldGltZXMgdGVsbCB0aGF0IHlvdSdyZSBub3QgZmluaXNoZWQgeWV0CgpgYGB7ciBldmFsPUZ9CjEwICsKYGBgCgpIb3cgdG8gY3JlYXRlIGEgKnZhcmlhYmxlKj8gVmFyaWFibGUgYXNzaWdubWVudCB1c2luZyBgPC1gIGFuZCBgPWAuIE5vdGUgdGhhdCBSIGlzIGNhc2Ugc2Vuc2l0aXZlIGZvciBldmVyeXRoaW5nCgpgYGB7cn0KcGF5IDwtIDI1MAoKbW9udGggPSAxMgoKcGF5ICogbW9udGgKCnNhbGFyeSA8LSBwYXkgKiBtb250aApgYGAKCgpGZXcgcG9pbnRzIGluIG5hbWluZyB2YXJpYWJsZXMgYW5kIHZlY3RvcnM6IHVzZSBzaG9ydCwgaW5mb3JtYXRpdmUgd29yZHMsIGtlZXAgc2FtZSBtZXRob2QgKGUuZy4sIHlvdSBjYW4gdXNlIGNhcGl0YWwgbGV0dGVycyBidXQgaXQgaXMgbm90IHJlY29tbWVuZGVkLCB1c2Ugb25seSBfIG9yIC4gKS4KCiMjIEZ1bmN0aW9uIApGdW5jdGlvbiBpcyBhIHNldCBvZiBzdGF0ZW1lbnRzIGNvbWJpbmVkIHRvZ2V0aGVyIHRvIHBlcmZvcm0gYSBzcGVjaWZpYyB0YXNrLiBXaGVuIHdlIHVzZSBhIGJsb2NrIG9mIGNvZGUgcmVwZWF0ZWRseSwgd2UgY2FuIGNvbnZlcnQgaXQgdG8gYSBmdW5jdGlvbi4gVG8gd3JpdGUgYSBmdW5jdGlvbiwgZmlyc3QsIHlvdSBuZWVkIHRvICpkZWZpbmUqIGl0OgoKYGBge3J9Cm15X211bHRpcGxpZXIgPC0gZnVuY3Rpb24oYSxiKXsKICByZXN1bHQgPSBhICogYgogIHJldHVybiAocmVzdWx0KQp9CmBgYAoKVGhpcyBjb2RlIGRvIG5vdGhpbmcuIFRvIGdldCBhIHJlc3VsdCwgeW91IG5lZWQgdG8gKmNhbGwqIGl0OgoKYGBge3J9Cm15X211bHRpcGxpZXIgKGE9MiwgYj00KQojIG9yOiBteV9tdWx0aXBsaWVyICgyLCA0KQpgYGAKCldlIGNhbiBzZXQgYSBkZWZhdWx0IHZhbHVlIGZvciBvdXIgYXJndW1lbnRzOgoKYGBge3J9Cm15X211bHRpcGxpZXIyIDwtIGZ1bmN0aW9uKGEsYj00KXsKICByZXN1bHQgPSBhICogYgogIHJldHVybiAocmVzdWx0KQp9CgpteV9tdWx0aXBsaWVyMiAoYT0yKQojIG9yOiBteV9tdWx0aXBsaWVyICgyKQojIG9yOiBteV9tdWx0aXBsaWVyICgyLCA2KQpgYGAKCkZvcnR1bmF0ZWx5LCB5b3UgZG8gbm90IG5lZWQgdG8gd3JpdGUgZXZlcnl0aGluZyBmcm9tIHNjcmF0Y2guIFIgaGFzIGxvdHMgb2YgYnVpbHQtaW4gZnVuY3Rpb25zIHRoYXQgeW91IGNhbiB1c2U6CmBgYHtyfQpyb3VuZCg1NC42Nzg3KQpyb3VuZCg1NC41Nzg3LCBkaWdpdHMgPSAyKQpgYGAKClVzZSBgP2AgYmVmb3JlIHRoZSBmdW5jdGlvbiBuYW1lIHRvIGdldCBzb21lIGhlbHAuIEZvciBleGFtcGxlLCBgP3JvdW5kYC4gWW91IHdpbGwgc2VlIG1hbnkgZnVuY3Rpb25zIGluIHRoZSByZXN0IG9mIHRoZSB3b3Jrc2hvcC4KCiMjIERhdGEgVHlwZXMKCmZ1bmN0aW9uIGBjbGFzcygpYCBpcyB1c2VkIHRvIHNob3cgd2hhdCBpcyB0aGUgdHlwZSBvZiBhIHZhcmlhYmxlLgoKCjEuICpMb2dpY2FsKjogYFRSVUVgLCBgRkFMU0VgIGNhbiBiZSBhYmJyZXZpYXRlZCBhcyBgVGAsIGBGYC4gIFRoZXkgaGFzIHRvIGJlIGNhcGl0YWwsICd0cnVlJyBpcyBub3QgYSBsb2dpY2FsIGRhdGE6CmBgYHtyfQpjbGFzcyhUUlVFKQpjbGFzcyhGKQpgYGAKCjIuICpOdW1lcmljKjogYWxsIG51bWJlcnMgZS5nLiA1LCAgMTAuNSwgIDExLDM3OyAgYSBzcGVjaWFsIHR5cGUgb2YgbnVtZXJpYyBpcyAiaW50ZWdlciIgd2hpY2ggaXMgbnVtYmVycyB3aXRob3V0IGRlY2ltYWwuIEludGVnZXJzIGFyZSBhbHdheXMgbnVtZXJpYywgYnV0IG51bWVyaWMgaXMgbm90IGFsd2F5cyBpbnRlZ2VyOgpgYGB7cn0KY2xhc3MoMikKY2xhc3MoMTMuNDYpCmBgYAoKMy4gKkNoYXJhY3Rlcio6IHRleHQgZm9yIGV4YW1wbGUsICJJIGxvdmUgUiIgb3IgIjQiIG9yICI0LjUiOgpgYGB7cn0KY2xhc3MoImhhIGhhIGhhIGhhIikKY2xhc3MoIjU2LjYiKQpjbGFzcygiVFJVRSIpCmBgYAoKQ2FuIHdlIGNoYW5nZSB0aGUgdHlwZSBvZiBkYXRhIGluIGEgdmFyaWFibGU/IFllcywgeW91IG5lZWQgdG8gdXNlIHRoZSBmdW5jdGlvbiBgYXMuLS0tKClgCgpgYGB7cn0KYXMubnVtZXJpYyhUUlVFKQphcy5jaGFyYWN0ZXIoNCkKYXMubnVtZXJpYygiNC41IikKYXMubnVtZXJpYygiSGVsbG8iKQpgYGAKCgojIyBEYXRhIFN0cnVjdHVyZXMKCgojIyMgVmVjdG9yIAoKV2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIG9uZSBudW1iZXIgb3IgbGV0dGVyIHN0b3JlZC4gVXNlIHRoZSBjb21iaW5lIGZ1bmN0aW9uIGMoKSBmb3IgdGhhdC4KCmBgYHtyfQpzYWxlIDwtIGMoMSwgMiwgMyw0LCA1LCA2LCA3LCA4LCA5LCAxMCkgIyBhbHNvIHNhbGUgPC0gYygxOjEwKQoKc2FsZSA8LSBjKDE6MTApCgpzYWxlICogc2FsZQpgYGAKCipTdWJzZXR0aW5nIGEgdmVjdG9yKjoKCmBgYHtyfQpkYXlzIDwtIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIsICJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLCAiRnJpZGF5IikKCmRheXNbMl0KZGF5c1stMl0KCmRheXNbYygyLCAzLCA0KV0KYGBgCgoKKiAqRXhlcmNpc2UqOiBDcmVhdGUgYSB2ZWN0b3IgbmFtZWQgYG15X3ZlY3RvcmAgd2l0aCBudW1iZXJzIGZyb20gMCB0byAxMDAwIGluIGl0IGFuZCBjYWxjdWxhdGUgbWVhbiwgbWVkaWFuLCBzZCwgbWluLCBtYXgsIGFuZCBzdW0gb2YgdGhhdCB2ZWN0b3I6CgpgYGB7cn0KbXlfdmVjdG9yIDwtICgwOjEwMDApCgptZWFuKG15X3ZlY3RvcikKbWVkaWFuKG15X3ZlY3RvcikKbWluKG15X3ZlY3RvcikKcmFuZ2UobXlfdmVjdG9yKQpjbGFzcyhteV92ZWN0b3IpCnN1bShteV92ZWN0b3IpCnNkKG15X3ZlY3RvcikKYGBgCgojIyMgTGlzdAoKTGlzdCBhbGxvd3MgeW91IHRvIGdhdGhlciBhIHZhcmlldHkgb2Ygb2JqZWN0cyB1bmRlciBvbmUgbmFtZSAodGhhdCBpcywgdGhlIG5hbWUgb2YgdGhlIGxpc3QpIGluIGFuIG9yZGVyZWQgd2F5LiBUaGVzZSBvYmplY3RzIGNhbiBiZSBtYXRyaWNlcywgdmVjdG9ycywgZGF0YSBmcmFtZXMsIGV2ZW4gb3RoZXIgbGlzdC4KCmBgYHtyfQpteV9saXN0ID0gbGlzdChzYWxlLCAxLCAzLCA0OjcsICJIRUxMTyIsICJoZWxsbyIsIEZBTFNFKQpteV9saXN0CmBgYAoKIyMjIEZhY3RvcgpGYWN0b3JzIHN0b3JlIHRoZSB2ZWN0b3IgYWxvbmcgd2l0aCB0aGUgZGlzdGluY3QgdmFsdWVzIG9mIHRoZSBlbGVtZW50cyBpbiB0aGUgdmVjdG9yIGFzIGxhYmVscy4gVGhlIGxhYmVscyBhcmUgYWx3YXlzIGNoYXJhY3RlciBpcnJlc3BlY3RpdmUgb2Ygd2hldGhlciBpdCBpcyBudW1lcmljIG9yIGNoYXJhY3Rlci4gRm9yIGV4YW1wbGUsIHZhcmlhYmxlIGdlbmRlciB3aXRoICJtYWxlIiBhbmQgImZlbWFsZSIgZW50cmllczoKCmBgYHtyfQpnZW5kZXIgPC0gYygibWFsZSIsICJtYWxlIiwgIm1hbGUiLCAiIGZlbWFsZSIsICJmZW1hbGUiLCAiZmVtYWxlIikKZ2VuZGVyIDwtIGZhY3RvcihnZW5kZXIpCmBgYAoKUiBub3cgdHJlYXRzIGdlbmRlciBhcyBhIG5vbWluYWwgKGNhdGVnb3JpY2FsKSB2YXJpYWJsZTogMT1mZW1hbGUsIDI9bWFsZSBpbnRlcm5hbGx5IChhbHBoYWJldGljYWxseSkuCmBgYHtyfQpzdW1tYXJ5KGdlbmRlcikKYGBgCgoqICpRdWVzdGlvbio6IHdoeSB3aGVuIHdlIHJhbiB0aGUgYWJvdmUgZnVuY3Rpb24gaS5lLiBzdW1tYXJ5KCksIGl0IHNob3dlZCB0aHJlZSBhbmQgbm90IHR3byBsZXZlbHMgb2YgdGhlIGRhdGE/ICpIaW50KjogcnVuICdnZW5kZXInLgoKYGBge3J9CmdlbmRlcgpgYGAKClNvLCBiZSBjYXJlZnVsIG9mIHNwYWNlcyEKCiogKkV4ZXJjaXNlKjogQ3JlYXRlIGEgZ2VuZGVyIGZhY3RvciB3aXRoIDMwIG1hbGUgYW5kIDQwIGZlbWFsZXMgKCpIaW50KjogdXNlIHRoZSBgcmVwKClgIGZ1bmN0aW9uKToKYGBge3J9CmdlbmRlciA8LSBjKHJlcCgibWFsZSIsMzApLCByZXAoImZlbWFsZSIsIDQwKSkKZ2VuZGVyIDwtIGZhY3RvcihnZW5kZXIpCmdlbmRlcgpgYGAKClRoZXJlIGFyZSB0d28gdHlwZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzOiBub21pbmFsIGFuZCBvcmRpbmFsLiBIb3cgdG8gY3JlYXRlIG9yZGVyZWQgZmFjdG9ycyAod2hlbiB0aGUgdmFyaWFibGUgaXMgbm9taW5hbCBhbmQgdmFsdWVzIGNhbiBiZSBvcmRlcmVkKT8gV2Ugc2hvdWxkIGFkZCB0d28gYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gdGhlIGBmYWN0b3IoKWAgZnVuY3Rpb246IGBvcmRlcmVkID0gVFJVRWAsIGFuZCBgbGV2ZWxzID0gYygibGV2ZWwxIiwgImxldmVsMiIpYC4gRm9yIGV4YW1wbGUsIHdlIGhhdmUgYSB2ZWN0b3IgdGhhdCBzaG93cyBwYXJ0aWNpcGFudHMnIGVkdWNhdGlvbiBsZXZlbC4KCmBgYHtyfQplZHU8LWMoMywyLDMsNCwxLDIsMiwzLDQpCgplZHVjYXRpb248LWZhY3RvcihlZHUsIG9yZGVyZWQgPSBUUlVFKQpsZXZlbHMoZWR1Y2F0aW9uKSA8LSBjKCJQcmltYXJ5IHNjaG9vbCIsImhpZ2ggc2Nob29sIiwiQ29sbGVnZSIsIlVuaSBncmFkdWF0ZWQiKQplZHVjYXRpb24KYGBgCgoqICpFeGVyY2lzZSo6IFdlIGhhdmUgYSBmYWN0b3Igd2l0aCBgcGF0aWVudGAgYW5kIGBjb250cm9sYCB2YWx1ZXMuIEhlcmUsIHRoZSBmaXJzdCBsZXZlbCBpcyBjb250cm9sIGFuZCB0aGUgc2Vjb25kIGxldmVsIGlzIHBhdGllbnQuIENoYW5nZSB0aGUgb3JkZXIgb2YgbGV2ZWxzLCBzbyBwYXRpZW50IHdvdWxkIGJlIHRoZSBmaXJzdCBsZXZlbDoKCmBgYHtyfQpoZWFsdGhfc3RhdHVzIDwtIGZhY3RvcihjKHJlcCgncGF0aWVudCcsNSkscmVwKCdjb250cm9sJyw1KSkpCmhlYWx0aF9zdGF0dXMKCmhlYWx0aF9zdGF0dXNfcmVvcmRlcmVkIDwtIGZhY3RvcihoZWFsdGhfc3RhdHVzLCBsZXZlbHMgPSBjKCdwYXRpZW50JywnY29udHJvbCcpKQpoZWFsdGhfc3RhdHVzX3Jlb3JkZXJlZApgYGAKCkZpbmFsbHksIGNhbiB5b3UgcmVsYWJlbCBib3RoIGxldmVscyB0byB1cHBlcmNhc2UgY2hhcmFjdGVycz8gKCpIaW50KjogY2hlY2sgYD9mYWN0b3JgKQoKYGBge3J9CmhlYWx0aF9zdGF0dXNfcmVsYWJlbGVkIDwtIGZhY3RvcihoZWFsdGhfc3RhdHVzLCBsZXZlbHMgPSBjKCdwYXRpZW50JywnY29udHJvbCcpLCBsYWJlbHMgPSBjKCdQYXRpZW50JywnQ29udHJvbCcpKQpoZWFsdGhfc3RhdHVzX3JlbGFiZWxlZApgYGAKCgojIyMgTWF0cmljZXMKQWxsIGNvbHVtbnMgaW4gYSBtYXRyaXggbXVzdCBoYXZlIHRoZSBzYW1lIG1vZGUobnVtZXJpYywgY2hhcmFjdGVyLCBldGMuKSBhbmQgdGhlIHNhbWUgbGVuZ3RoLiBJdCBjYW4gYmUgY3JlYXRlZCB1c2luZyBhIHZlY3RvciBpbnB1dCB0byB0aGUgbWF0cml4IGZ1bmN0aW9uLgoKYGBge3J9Cm15X21hdHJpeCA9IG1hdHJpeChjKDEsMiwzLDQsNSw2LDcsOCw5KSwgbnJvdyA9IDMsIG5jb2wgPSAzKQoKbXlfbWF0cml4CmBgYAoKIyMjIERhdGEgZnJhbWVzIAoKRGF0YSBmcmFtZXMgY2FuIGhvbGQgbnVtZXJpYywgY2hhcmFjdGVyIG9yIGxvZ2ljYWwgdmFsdWVzLiBXaXRoaW4gYSBjb2x1bW4gYWxsIGVsZW1lbnRzIGhhdmUgdGhlIHNhbWUgZGF0YSB0eXBlLCBidXQgZGlmZmVyZW50IGNvbHVtbnMgY2FuIGJlIG9mIGRpZmZlcmVudCBkYXRhIHR5cGUuIExldCdzIGNyZWF0ZSBhIGRhdGFmcmFtZToKCmBgYHtyfQppZCA8LSAxOjIwMApncm91cCA8LSBjKHJlcCgiUHN5Y2hvdGhlcmFweSIsIDEwMCksIHJlcCgiTWVkaWNhdGlvbiIsIDEwMCkpCnJlc3BvbnNlIDwtIGMocm5vcm0oMTAwLCBtZWFuID0gMzAsIHNkID0gNSksCiAgICAgICAgICAgICBybm9ybSgxMDAsIG1lYW4gPSAyNSwgc2QgPSA1KSkKCm15X2RhdGFmcmFtZSA8LWRhdGEuZnJhbWUoUGF0aWVudCA9IGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRyZWF0bWVudCA9IGdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3BvbnNlID0gcmVzcG9uc2UpCmBgYAoKV2UgYWxzbyBjb3VsZCBoYXZlIGRvbmUgdGhlIGJlbG93CgpgYGB7cn0KbXlfZGF0YWZyYW1lIDwtZGF0YS5mcmFtZShQYXRpZW50ID0gYygxOjIwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID0gYyhyZXAoIlBzeWNob3RoZXJhcHkiLCAxMDApLCByZXAoIk1lZGljYXRpb24iLCAxMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNwb25zZSA9IGMocm5vcm0oMTAwLCBtZWFuID0gMzAsIHNkID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJub3JtKDEwMCwgbWVhbiA9IDI1LCBzZCA9IDUpKSkKYGBgCgpJbiBsYXJnZSBkYXRhIHNldHMsIHRoZSBmdW5jdGlvbiBoZWFkKCkgZW5hYmxlcyB5b3UgdG8gc2hvdyB0aGUgZmlyc3Qgb2JzZXJ2YXRpb25zIG9mIGEgZGF0YSBmcmFtZXMuIFNpbWlsYXJseSwgdGhlIGZ1bmN0aW9uIHRhaWwoKSBwcmludHMgb3V0IHRoZSBsYXN0IG9ic2VydmF0aW9ucyBpbiB5b3VyIGRhdGEgc2V0LgoKYGBge3IgZXZhbD1GfQpoZWFkKG15X2RhdGFmcmFtZSkgCnRhaWwobXlfZGF0YWZyYW1lKQpgYGAKCmBgYHtyIGVjaG89Rn0KaGVhZChteV9kYXRhZnJhbWUpICU+JQogIG11dGF0ZShgIGA9IGMoMTo2KSkgJT4lCiAgc2VsZWN0KGAgYCwgUGF0aWVudCwgVHJlYXRtZW50LAlSZXNwb25zZSkgJT4lCiAga25pdHI6OmthYmxlKCkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgp0YWlsKG15X2RhdGFmcmFtZSklPiUKICBrbml0cjo6a2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgpTaW1pbGFyIHRvIHZlY3RvcnMgYW5kIG1hdHJpY2VzLCBicmFja2V0cyBbXSBhcmUgdXNlZCB0byBzZWxlY3RzIGRhdGEgZnJvbSByb3dzIGFuZCBjb2x1bW5zIGluIGRhdGEuZnJhbWVzOgoKYGBge3J9Cm15X2RhdGFmcmFtZVszNSwgM10KYGBgCgoqICpFeGVyY2lzZSo6IEhvdyBjYW4gd2UgZ2V0IGFsbCBjb2x1bW5zLCBidXQgb25seSBmb3IgdGhlIGZpcnN0IDEwIHBhcnRpY2lwYW50cz8KCmBgYHtyIGV2YWw9Rn0KbXlfZGF0YWZyYW1lWzE6MTAsIF0KYGBgCgpgYGB7ciBlY2hvPUZ9CmtuaXRyOjprYWJsZShteV9kYXRhZnJhbWVbMToxMCwgXSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgpgYGAKSG93IHRvIGdldCBvbmx5IHRoZSBSZXNwb25zZSBjb2x1bW4gZm9yIGFsbCBwYXJ0aWNpcGFudHM/CgpgYGB7cn0KbXlfZGF0YWZyYW1lWyAsIDNdCmBgYAoKQW5vdGhlciBlYXNpZXIgd2F5IGZvciBzZWxlY3RpbmcgcGFydGljdWxhciBpdGVtcyBpcyB1c2luZyB0aGVpciBuYW1lcyB0aGF0IGlzIG1vcmUgaGVscGZ1bCB0aGFuIG51bWJlciBvZiB0aGUgcm93cyBpbiBsYXJnZSBkYXRhIHNldHM6CmBgYHtyIGV2YWw9Rn0KbXlfZGF0YWZyYW1lWyAsICJSZXNwb25zZSJdCiMgT1I6Cm15X2RhdGFmcmFtZSRSZXNwb25zZQoKYGBgCgpTbyBmYXIsIHdlIGNyZWF0ZWQgZGF0YWZyYW1lcyB1c2luZyBgZGF0YS5mcmFtZWAgZnVuY3Rpb24gZnJvbSB0aGUgYmFzZSBSLiBIb3dldmVyLCBhIGJldHRlciB3YXkgdG8gY3JlYXRlIGRhdGFmcmFtZXMgaXMgdG8gdXNlIHRoZSBgdGliYmxlYCBmdW5jdGlvbiBmcm9tIHRpZHl2ZXJzZSAoc2VlIFtoZXJlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpYmJsZXMuaHRtbCkpLgoKIyBEYXRhIENsZWFuaW5nCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdlbnZpcm9ubWVudGFsLWRhdGEtc2NpZW5jZS1yNGRzLWdlbmVyYWwucG5nJykpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ2NyYWNrZWRfc2V0d2QucG5nJykpCmBgYAoKTm93LCBzdXBwb3NlIHdlIHJhbiBhbiBleHBlcmltZW50IHdpdGggMTQxIGRlcHJlc3NlZCBwYXRpZW50cy4gUGFydGljaXBhbnRzIHdlcmUgcmFuZG9tbHkgYXNzaWduZWQgaW50byB0d28gdHJlYXRtZW50IGdyb3VwczogQ0JUIG9yIFBzeWNob2R5bmFtaWMgcHN5Y2hvdGhlcmFweS4gV2UgbWVhc3VyZWQgc2VsZi1yZXBvcnQgZGVwcmVzc2lvbiBzY29yZXMgYXQgNSBkaWZmZXJlbnQgc3RhZ2VzIG9mIHRyZWF0bWVudDogCgotIFN0YWdlIDE6IEJlZm9yZSBzdGFydGluZyBhbnkgdHJlYXRtZW50LiBJdCBpcyBvdXIgYmFzZSBzdGFnZSAocHJlLXRlc3QpCi0gU3RhZ2UgMjogQWZ0ZXIgNSBzZXNzaW9ucyBvZiBwc3ljaG90aGVyYXB5IChwb3N0LXRlc3QxKQotIFN0YWdlIDM6IEFmdGVyIDEwIHNlc3Npb25zIG9mIHBzeWNob3RoZXJhcHkgKHBvc3QtdGVzdDIpCi0gU3RhZ2UgNDogQXQgdGhlIGVuZCBvZiB0aGUgdHJlYXRtZW50IChwb3N0LXRlc3QzKQotIFN0YWdlIDU6IFRocmVlIG1vbnRocyBhZnRlciB0aGUgdHJlYXRtZW50IChwb3N0LXRlc3Q0KQoKbGV0J3MgcmVhZCBhbmQgY2hlY2sgdGhlIHVuY2xlYW5lZCBkYXRhLiBCdXQsIGZpcnN0IHRoaW5nIGZpcnN0LiBsZXQncyBpbnN0YWxsIGFuZCB0aGVuIGxvYWQgdGhlIHRpZHl2ZXNlIHBhY2thZ2UuIFdlIGFsc28gbmVlZCBzb21lIG90aGVyIHBhY2thZ2VzOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KCiMgSW5zdGFsbCBpdAppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQoKIyBBbmQgdGhlbiBsb2FkIGl0CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBMb2FkIG90aGVyIHBhY2thZ2VzIHRoYXQgeW91IGhhdmUgYWxyZWFkeSBpbnN0YWxsZWQKbGlicmFyeShoZXJlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoYWZleCkKbGlicmFyeShlbW1lYW5zKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KHNraW1yKQojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCmxpYnJhcnkoImNvcnJlbGF0aW9uIikKb3B0aW9ucyhzY2lwZW49OTk5KSAjIHR1cm4gb2ZmIHNjaWVudGlmaWMgbm90YXRpb25zCm9wdGlvbnMoY29udHJhc3RzID0gYygnY29udHIuc3VtJywnY29udHIucG9seScpKSAjIHNldCB0aGUgY29udHJhc3Qgc3VtIGdsb2JhbGx5IApvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KIyByZWFkIHRoZSByYXcgZGF0YQpyYXdfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJyYXdfZGF0YSIsInJhd19kYXRhX2V4cDEuY3N2IikpCmhlYWQocmF3X2RhdGEpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGVjaG89Rn0KIyByZWFkIHRoZSByYXcgZGF0YQpyYXdfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJyYXdfZGF0YSIsInJhd19kYXRhX2V4cDEuY3N2IikpCgprbml0cjo6a2FibGUoaGVhZChyYXdfZGF0YSkpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQpgYGAKCiogKkV4ZXJjaXNlKjogVGhlcmUgaXMgYSBkYXRhc2V0IGluIHRoZSBgY2xlYW5lZF9kYXRhYCBmb2xkZXIgbmFtZWQgYHVuaWNlZl91NW1yLmNzdmAuIFJlYWQgdGhlIGRhdGFzZXQgdXNpbmcgYHJlYWRfY3N2YCBhbmQgYGhlcmVgLgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KdW5pY2VmX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidW5pY2VmX3U1bXIuY3N2IikpCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywndGlkeWRhdGFfMy5qcGcnKSkKYGBgCgpJbiBvcmRlciB0byBjbGVhbiB0aGUgZGF0YSwgd2UgdXNlICp0aWR5dmVyc2UqIHdoaWNoIGlzIGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyB0byB3b3JrIHdpdGggZGF0YS4gT25lIG9mIHRoZSB0aWR5dmVyc2UgcGFja2FnZXMgdGhhdCB3ZSB1c2UgcmVndWxhcmx5IGlzIGBkcGx5cmAgd2hpY2ggaW5jbHVkZXMgc2V2ZXJhbCBmdW5jdGlvbnM6CgotIGBtdXRhdGUoKWAgYWRkcyBuZXcgdmFyaWFibGVzIG9yIGNoYW5nZSBleGlzdGluZyBvbmVzLgotIGBzZWxlY3QoKWAgcGljayB2YXJpYWJsZXMgKGNvbHVtbnMpIGJhc2VkIG9uIHRoZWlyIG5hbWVzLgotIGBmaWx0ZXIoKWAgcGlja3MgY2FzZXMgKHJvd3MpIGJhc2VkIG9uIHRoZWlyIHZhbHVlcy4KLSBgc3VtbWFyaXNlKClgIGdpdmVzIGEgc2luZ2xlIHNpbmdsZSBzdW1tYXJ5IG9mIHRoZSBkYXRhIChlLmcuLCBtZWFuLCBjb3VudHMsIGV0Yy4pCi0gYGFycmFuZ2UoKWAgY2hhbmdlcyB0aGUgb3JkZXJpbmcgb2YgdGhlIHJvd3MuCi0gYGdyb3VwX2J5KClgIGRpdmlkZXMgeW91ciBkYXRhZnJhbWUgaW50byBncm91cGVkIGRhdGFmcmFtZXMgYW5kIGFsbG93IHlvdSB0byBkbyBlYWNoIG9mIHRoZSBhYm92ZSBvcGVyYXRpb25zIChleGNlcHQgZm9yIGBhcnJhbmdlYCkgb24gZXZlcnkgb25lIG9mIHRoZW0gc2VwYXJhdGVseS4KCiMjIFNlbGVjdAoKUGljayBgc3ViamVjdGAsIGBhZ2VgLCBhbmQgYGdlbmRlcmAgY29sdW1uczoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPUZ9CnNlbGVjdGVkX2RhdGEgPC0gc2VsZWN0KHJhd19kYXRhLCBzdWJqZWN0LCBhZ2UsIGdlbmRlcikKYGBgCgojIyBGaWx0ZXIKTm93LCBkbyB0aGUgZm9sbG93aW5nIHRhc2tzOiBwaWNrIGFsbCB0aGUgbWFsZSBwYXJ0aWNpcGFudHMsIHBpY2sgYWxsIHRoZSBtYWxlIHBhcnRpY2lwYW50cyAqKm9yKiogdGhvc2UgZ3JlYXRlciB0aGFuIDI1IHllYXJzIG9sZCwgYW5kIGZpbmFsbHkgcGljayBhbGwgbWFsZSBwYXJ0aWNpcGFudHMgKiphbmQqKiB0aG9zZSBncmVhdGVyIHRoYW4gMjUgeWVhcnMgb2xkOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KIyBmaWx0ZXIgYWxsIG1hbGVzCmZpbF9tYWxlIDwtIGZpbHRlcihyYXdfZGF0YSwgZ2VuZGVyID09ICJNYWxlIikKIyBmaWx0ZXIgbWFsZXMgYW5kIG9sZGVyIHRoYW4gMjUKZmlsX21hbGVfYW5kX2cyNSA8LSBmaWx0ZXIocmF3X2RhdGEsIGdlbmRlciA9PSAiTWFsZSIgJiBhZ2UgPiAyNSApCiMgZmlsdGVyIG1hbGVzIG9yIG9sZGVyIHRoYW4gMjUKZmlsX21hbGVfb3JfZzI1IDwtIGZpbHRlcihyYXdfZGF0YSwgZ2VuZGVyID09ICJNYWxlIiB8IGFnZSA+IDI1ICkKYGBgCgojIyBBcnJhbmdlIApBcnJhbmdlIChvcmRlcikgeW91ciBkYXRhZnJhbWUgYmFzZWQgb24gdGhlIGFnZSwgb25jZSBpbiBhbiBhc2NlbmRpbmcgb3JkZXIgKHlvdW5nZXJzIGZpcnN0KSBhbmQgb25jZSBiYXNlZCBvbiBkZXNjZW5kaW5nIG9yZGVyIChvbGRlcnMgZmlyc3QpOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CiMgb3JkZXIgcGFydGljaXBhbnRzIGJhc2VkIG9uIHRoZWlyIGFnZQphcnJhbmdlZF9kYXRhIDwtIGFycmFuZ2UocmF3X2RhdGEsIGFnZSkKIyBvcmRlciBwYXJ0aWNpcGFudHMgYmFzZWQgb24gdGhlaXIgYWdlIChkZXNjZW5kZWluZykKYXJyYW5nZWRfZGVzY2VuZGluZyA8LSBhcnJhbmdlKHJhd19kYXRhLCBkZXNjKGFnZSkpCmBgYAoKIyMgTXV0YXRlCkNyZWF0ZSBhIGNvbHVtbiB0byBzaG93IGlmIHRoZSBwYXJ0aWNpcGFudCBoYXMgZmluaXNoZWQgdGhlIHRhc2sgb3Igbm90OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbXV0YXRlZF9kYXRhIDwtIG11dGF0ZSAocmF3X2RhdGEsIGZpbmlzaGVkPSBjYXNlX3doZW4ocHJvZ3Jlc3M9PTEwMH4gIlllcyIsVH4gIk5vIikpCmBgYAoKIyMgU3VtbWFyaXNlClN1bW1hcml6ZSBwYXJ0aWNpcGFudHMgYWdlIGFuZCBzZDoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnN1bW1hcmlzZShyYXdfZGF0YSwgbWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKQpgYGAKCiMjIFBpcGUgT3BlcmF0b3JzCkEgbmV3IGZ1bmN0aW9uOiAqKnBpcGUgb3BlcmF0b3JzKiogYCU+JWAgcGlwZXMgYSB2YWx1ZSBpbnRvIHRoZSBuZXh0IGZ1bmN0aW9uOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnJhd19kYXRhICU+JSAKICBzdW1tYXJpc2UoLiwgbWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgICBzZD0gc2QgKGFnZSwgbmEucm09VCkpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpyYXdfZGF0YSAlPiUgCiAgc3VtbWFyaXNlKG1lYW49IG1lYW4oYWdlLCBuYS5ybT1UKSwKICAgICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKQpgYGAKCkNhbGN1bGF0ZSB0aGUgYWdlIG1lYW4gb2YgeW91bmdlciB0aGFuIDI1IHBhcnRpY2lwYW50cyBvbmx5OgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnJhd19kYXRhICU+JSAKICBmaWx0ZXIgKGFnZSA8IDI1KSAlPiUKICBzdW1tYXJpc2UobWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgICBzZD0gc2QgKGFnZSwgbmEucm09VCkpCmBgYAoKIyMgR3JvdXAgQnkKCkNhbGN1bGF0ZSB0aGUgYWdlIG1lYW4gb2YgeW91bmdlciB0aGFuIDI1IHBhcnRpY2lwYW50cyAgZm9yIGVhY2ggZ2VuZGVyIHNlcGFyYXRlbHk6CgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcmF3X2RhdGEgJT4lIAogIGZpbHRlciAoYWdlIDwgMjUpICU+JQogIGdyb3VwX2J5KGdlbmRlcikgJT4lCiAgc3VtbWFyaXNlKG1lYW49IG1lYW4oYWdlLCBuYS5ybT1UKSwKICAgICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKSAlPiUKICB1bmdyb3VwICgpCmBgYCAgICAgICAgIAoKCiogKkV4ZXJjaXNlKjogQ3JlYXRlIGEgY29sdW1uIHRvIHNob3cgaWYgcGFydGljaXBhbnQgaXMgb2xkZXIgdGhhbiAyMyBvciBub3QgYW5kIHRoZW4gY2FsY3VsYXRlIHNsZWVwIHF1YWxpdHkgKGBzbGVlcF9xdWFsaXR5YCkgbWVhbiBmb3IgZWFjaCBncm91cCBzZXBhcmF0ZWx5OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcmF3X2RhdGEgJT4lCiAgbXV0YXRlKGFnZV9ncm91cCA9IGNhc2Vfd2hlbihhZ2UgPiAyMyB+ICJncmVhdGVyIHRoYW4gMjMiLCBUfiAieW91bmdlciB0aGFuIDIzIikpICU+JQogIGdyb3VwX2J5KGFnZV9ncm91cCkgJT4lCiAgc3VtbWFyaXNlKHNsZWVwX3F1YWxpdHkgPSBtZWFuKHNsZWVwX3F1YWxpdHksIG5hLnJtPVQpKQpgYGAgICAgIAoKKiAqRXhlcmNpc2UqOiBBZGQgdGhlIGFueGlldHkgdG90YWwgc2NvcmUgKHN1bSkgdG8gdGhlIGRhdGFmcmFtZSBhbmQgdGhlbiBjb252ZXJ0IHN1YmplY3QgY29sdW1uIHRvIGZhY3RvcjoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmFueGlldHlfZGF0YSA8LSByYXdfZGF0YSAlPiUKICBtdXRhdGUoYW54aWV0eV90b3RhbD0gYW54aWV0eTErYW54aWV0eTIrYW54aWV0eTMrYW54aWV0eTQrYW54aWV0eTUrYW54aWV0eTYrYW54aWV0eTcrYW54aWV0eTgpICU+JQogIG11dGF0ZShzdWJqZWN0PSBmYWN0b3Ioc3ViamVjdCkpCmBgYCAKCiMjIFBpdm90aW5nCgpOZXh0LCB3ZSB3YW50IHRvIHBpdm90IG91ciBkYXRhIHRvIHN3aXRjaCBiZXR3ZWVuIGxvbmcgYW5kIHdpZGUgZm9ybWF0OgoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywndGlkeWRhdGFfMS5qcGcnKSkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KCiMgTWFrZSB5b3UgZGF0YSBsb25nCmxvbmdfZGF0YSA8LSByYXdfZGF0YSAlPiUKICBzZWxlY3Qoc3ViamVjdCwgc3RhZ2UxX2NidDpzdGFnZTVfY2J0LHN0YWdlMV9keW5hbWljOnN0YWdlNV9keW5hbWljKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoc3RhZ2UxX2NidDpzdGFnZTVfZHluYW1pYyksIG5hbWVzX3RvID0gJ3N0YWdlJywgdmFsdWVzX3RvID0gJ2RlcHJlc3Npb25fc2NvcmUnKQoKIyBNYWtlIHlvdSBkYXRhIHdpZGUKd2lkZV9kYXRhIDwtIGxvbmdfZGF0YSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc3RhZ2UsIHZhbHVlc19mcm9tPSBkZXByZXNzaW9uX3Njb3JlKQoKYGBgCgoqICpFeGVyY2lzZSo6IENvbnZlcnQgdGhlIFVOSUNFRiBkYXRhc2V0IHRvIGxvbmcgYW5kIHdpZGUgZm9ybWF0czoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnVuaWNlZl9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsInVuaWNlZl91NW1yLmNzdiIpKQoKbGlicmFyeShqYW5pdG9yKQp1bmljZWZfZGF0YV9jbGVhbmVkIDwtIHVuaWNlZl9kYXRhICU+JQogIGNsZWFuX25hbWVzKCkKCnVuaWNlZl9sb25nX2RhdGEgPC0gdW5pY2VmX2RhdGFfY2xlYW5lZCAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHU1bXJfMTk1MDp1NW1yXzIwMTUpLCBuYW1lc190byA9ICd5ZWFyJywgdmFsdWVzX3RvID0gJ3U1bXInKQp1bmljZWZfd2lkZWdfZGF0YSA8LSB1bmljZWZfbG9uZ19kYXRhICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gJ3llYXInLCB2YWx1ZXNfZnJvbSA9ICd1NW1yJykKYGBgCgoqTm90ZSo6IFRoZSBjb2RlcyBmb3IgdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHdlcmUgdGFrZW4gZnJvbSBbdGhpcyBibG9nIHBvc3RdKGh0dHBzOi8vc2VqZGVteXIuZ2l0aHViLmlvL3ItdHV0b3JpYWxzL2Jhc2ljcy93aWRlLWFuZC1sb25nLykgd3JpdHRlbiBieSBTaW1vbiBFamRlbXlyLgoKTm93LCBsZXQncyBkbyBzb21lIGNsZWFuaW5nIHVzaW5nIGBkcGx5cmAsIGB0aWR5cmAgYW5kIG90aGVyIGB0aWR5dmVyc2VgIGxpYnJhcmllczogCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPUZ9CmNsZWFuZWRfZGF0YSA8LSByYXdfZGF0YSAlPiUgCiAgZmlsdGVyKHByb2dyZXNzID09IDEwMCkgJT4lICMgZmlsdGVyIG91dCB1bmZpbmlzaGVkIHBhcnRpY2lwYW50cwogIHNlbGVjdCgtY29uc2VudF9mb3JtKSAlPiUgI3JlbW92ZSBzb21lIHVzZWxlc3MgY29sdW1ucwogICMgY3JlYXRlIGEgdG90YWwgc2NvcmUgZm9yIG91ciBxdWVzdGlvbm5haXJlCiAgbXV0YXRlKGFueGlldHlfdG90YWw9IGFueGlldHkxK2FueGlldHkyK2FueGlldHkzK2FueGlldHk0K2FueGlldHk1K2FueGlldHk2K2FueGlldHk3K2FueGlldHk4KSAlPiUKICBzZWxlY3QoLWFueGlldHkxOi1hbnhpZXR5OCkgJT4lCiAgIyBtYWtlIG91ciBkYXRhZnJhbWUgbG9uZwogIHBpdm90X2xvbmdlcihjb2xzID0gYyhzdGFnZTFfY2J0OnN0YWdlNV9jYnQsc3RhZ2UxX2R5bmFtaWM6c3RhZ2U1X2R5bmFtaWMpLG5hbWVzX3RvID0gJ3N0YWdlJyx2YWx1ZXNfdG8gPSAnZGVwcmVzc2lvbl9zY29yZScpICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YWdlLCB2YWx1ZXNfZnJvbT0gZGVwcmVzc2lvbl9zY29yZSkgIyB0aGlzIGNvZGUgY2hhbmdlIG91ciBkYXRhZnJhbWUgYmFjayB0byB3aWRlCiAgZmlsdGVyKCFpcy5uYShkZXByZXNzaW9uX3Njb3JlKSkgJT4lICNyZW1vdmUgcm93cyB3aXRoIGRlcHJlc3Npb25fc2NvcmUgPT0gTkEKICBtdXRhdGUoc3RhZ2U9IGdzdWIoIl8uKiIsICIiLCBzdGFnZSkpICU+JQogIHNlbGVjdCAoc3ViamVjdCwgYWdlLCBnZW5kZXIsIGdyb3VwLCBzdGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBlY2hvPUZ9CmNsZWFuZWRfZGF0YSA8LSByYXdfZGF0YSAlPiUgCiAgZmlsdGVyKHByb2dyZXNzID09IDEwMCkgJT4lICMgZmlsdGVyIG91dCB1bmZpbmlzaGVkIHBhcnRpY2lwYW50cwogIHNlbGVjdCgtY29uc2VudF9mb3JtKSAlPiUgI3JlbW92ZSBzb21lIHVzZWxlc3MgY29sdW1ucwogICMgY3JlYXRlIGEgdG90YWwgc2NvcmUgZm9yIG91ciBxdWVzdGlvbm5haXJlCiAgbXV0YXRlKGFueGlldHlfdG90YWw9IGFueGlldHkxK2FueGlldHkyK2FueGlldHkzK2FueGlldHk0K2FueGlldHk1K2FueGlldHk2K2FueGlldHk3K2FueGlldHk4KSAlPiUKICBzZWxlY3QoLWFueGlldHkxOi1hbnhpZXR5OCkgJT4lCiAgIyBtYWtlIG91ciBkYXRhZnJhbWUgbG9uZwogIHBpdm90X2xvbmdlcihjb2xzID0gYyhzdGFnZTFfY2J0OnN0YWdlNV9jYnQsc3RhZ2UxX2R5bmFtaWM6c3RhZ2U1X2R5bmFtaWMpLG5hbWVzX3RvID0gJ3N0YWdlJyx2YWx1ZXNfdG8gPSAnZGVwcmVzc2lvbl9zY29yZScpICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YWdlLCB2YWx1ZXNfZnJvbT0gZGVwcmVzc2lvbl9zY29yZSkgIyB0aGlzIGNvZGUgY2hhbmdlIG91ciBkYXRhZnJhbWUgYmFjayB0byB3aWRlCiAgZmlsdGVyKCFpcy5uYShkZXByZXNzaW9uX3Njb3JlKSkgJT4lICNyZW1vdmUgcm93cyB3aXRoIGRlcHJlc3Npb25fc2NvcmUgPT0gTkEKICBtdXRhdGUoc3RhZ2U9IGdzdWIoIl8uKiIsICIiLCBzdGFnZSkpICU+JQogIHNlbGVjdCAoc3ViamVjdCwgYWdlLCBnZW5kZXIsIGdyb3VwLCBzdGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pCgprbml0cjo6a2FibGUoaGVhZChjbGVhbmVkX2RhdGEpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRiklPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjc4MHB4IikKYGBgCgpPaywgbm93IHRoZSBkYXRhIGlzIGNsZWFuIGFuZCB0aWR5IHdoaWNoIG1lYW5zOgoKPiAxLiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uLgoyLiBFYWNoIG9ic2VydmF0aW9uIGZvcm1zIGEgcm93LgozLiBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUgKFtXaWNraGFtXShodHRwczovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy90aWR5LWRhdGEucGRmKSwgMjAxNCkuCgoKQ2hlY2sgdGhlIGRhdGFmcmFtZSBhbmQgYWxsIHRoZSBkYXRhIHR5cGVzOgpgYGB7cn0Kc3RyKGNsZWFuZWRfZGF0YSkKYGBgCgpGaW5hbGx5LCB3ZSBzYXZlIG91ciBkYXRhIHRvIHRoZSBgY2xlYW5lZF9kYXRhYCBmb2xkZXIuCgpgYGB7cn0Kd3JpdGVfY3N2KGNsZWFuZWRfZGF0YSwgaGVyZSgiY2xlYW5lZF9kYXRhIiwiY2xlYW5lZF9kYXRhX2V4cDEuY3N2IikpCmBgYAoKCiMgRGF0YSBWaXN1YWxpemF0aW9uCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdnZ3Bsb3QyX21hc3RlcnBpZWNlLnBuZycpKQpgYGAKCkJlZm9yZSBzdGFydGluZyB0aGUgZ2dwbG90LCBsZXQncyB0cnkgYSB2aXN1YWxpemF0aW9uIHVzaW5nIGEgZnVuY3Rpb24gZnJvbSB0aGUgQmFzZSBSIHRoZSBwbG90KCkgZnVuY3Rpb24gc2hvd3MgdGhlIGFzc29jaWF0aW9uIG9mIGVhY2ggdmFyaWFibGUgYWdhaW5zdCB0aGUgb3RoZXIgb25lIGluIGEgZGF0YSBoYW5keSBmb3IgZGF0YSB3aXRoIGZldyBudW1iZXIgb2YgdmFyaWFibGVzIHRvIHNlZSBpZiB0aGVyZSBhcmUgYW55IHBhdHRlcm5zCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTR9CgpleGFtX2RhdGE8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJjbGVhbmVkX2RhdGEiLCAiZXhhbV9kYXRhLmNzdiIpKQoKcGxvdCh4ID0gZXhhbV9kYXRhJEFueGlldHksIHkgPSBleGFtX2RhdGEkRXhhbSkKCmBgYAoKVGhlIGNvZGUgYWxzbyB3b3JrcyB3aXRob3V0IHdyaXRpbmcgeCBhbmQgeSwgaG93ZXZlciwgd3JpdGluZyB0aGVtIGlzIHN0cm9uZ2x5IHJlY29tbWVuZGVkCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CgpwbG90KGV4YW1fZGF0YSRBbnhpZXR5LCBleGFtX2RhdGEkRXhhbSkKYGBgCgpgZ2dwbG90YCwgdGhlIGdnIGluIGdncGxvdCBzdGFuZHMgZm9yIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIEdyYW1tYXIgb2YgZ3JhcGhpY3MgYmFzaWNhbGx5IHNheXMgYW55IGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiBkYXRhLCBjYW4gYmUgcHJvZHVjZWQgYnkgYSBzZXJpZXMgb2YgbGF5ZXJzLiBZb3UgY2FuIHRoaW5rIG9mIGEgbGF5ZXIgYXMgYSBwbGFzdGljIHRyYW5zcGFyZW5jeS4gTGV0cyBkcmF3IHRoZSBzYW1lIHBsb3QgdXNpbmcgZ2dwbG90LiBBbHdheXMsIG1lbnRpb24gdGhlIGRhdGEgd2UgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aC4KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBFeGFtLCB5ID0gQW54aWV0eSkpCmBgYAoKCi0gYGFlc2A6IGFlcyB3aGljaCBzdGFuZHMgZm9yIGFlc3RoZXRpY3MgaXMgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhIHZhcmlhYmxlIGluIHlvdXIgZGF0YXNldCBhbmQgYW4gYXNwZWN0IG9mIHRoZSBwbG90IHRoYXQgaXMgZ29pbmcgdG8gdmlzdWFsbHkgY29udmV5IHRoZSBpbmZvcm1hdGlvbiB0byB0aGUgcmVhZGVyCgotIFZpc3VhbCBlbGVtZW50cyBhcmUga25vd24gYXMgZ2VvbXMgKHNob3J0IGZvciAnZ2VvbWV0cmljIG9iamVjdHMnKSBpbiBnZ3Bsb3QgMi4gV2hlbiB3ZSBkZWZpbmUgYSBsYXllciwgd2UgaGF2ZSB0byB0ZWxsIFIgd2hhdCBnZW9tIHdlIHdhbnQgZGlzcGxheWVkIG9uIHRoYXQgbGF5ZXIgKGRvIHdlIHdhbnQgYSBiYXIsIGxpbmUgZG90LCBldGMuPykKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHkpKSsgZ2VvbV9wb2ludCgpCmBgYAoKU28sIGxldHMgdHJ5IHNvbWUgb2YgdGhlbSBoZXJlIGxpa2Ugc2hhcGUgYW5kIHNpemUuIEJlIGNhcmVmdWwgd2l0aCB0aGUgKyBzaWduLCBpZiB5b3UgY2xpbmsgZW50ZXIgZm9yIHRoZSBuZXh0IHBhcnQgb2YgdGhlIGNvZGUsIHRoZSArIHNpZ24gc2hvdWxkIG5vdCBnbyB0byB0aGUgbmV4dCBsaW5lCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CmdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEV4YW0sIHkgPSBBbnhpZXR5KSkrCiAgZ2VvbV9wb2ludChzaXplID0gMiwgc2hhcGUgPSA4KQpgYGAKClRoZSBjdXJyZW50IHBsb3QgaXMgbm90IHZlcnkgaW5mb3JtYXRpdmUgYWJvdXQgdGhlIHBhdHRlcm5zIGZvciBlYWNoIGdlbmRlci4KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBFeGFtLCB5ID0gQW54aWV0eSwgY29sb3IgPSBHZW5kZXIpKSsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBzaGFwZSA9IDEwKQoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHksIGNvbG9yID0gR2VuZGVyLCBzaGFwZSA9IEdlbmRlcikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gMTApCmBgYAoKUXVlc3Rpb246IHdoeSB0aGUgYWJvdmUgY29kZSBkb2Vzbid0IG1ha2UgYW55IGNoYW5nZT8KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHksIGNvbG9yID0gR2VuZGVyLCBzaGFwZSA9IEdlbmRlcikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpCmBgYAoKQ2FuIGFzc2lnbiB0aGUgZmlyc3QgbGF5ZXIgdG8gYSB2YXJpYWJsZSB0byByZWR1Y2UgdGhlIGxlbmd0aCBvZiBjb2RlcyBmb3IgbmV4dCBsYXllcnMuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9Ck15X2dyYXBoIDwtIGdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEV4YW0sIHkgPSBBbnhpZXR5KSkKCk15X2dyYXBoICsgZ2VvbV9wb2ludCgpCmBgYAoKbGV0cyBhZGQgYSBsaW5lIHRvIHRoZSBjdXJyZW50IGdyYXBoCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmBgYAoKQWVzdGhldGljcyBjYW4gYmUgc2V0IGZvciBhbGwgbGF5ZXJzIG9mIHRoZSBwbG90IChpLmUuLCBkZWZpbmVkIGluIHRoZSBwbG90IGFzIGEgd2hvbGUpIG9yIGNhbiBiZSBzZXQgaW5kaXZpZHVhbGx5IGZvciBlYWNoIGdlb20gaW4gYSBwbG90LgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aCgpCgpNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBHZW5kZXIpKQpgYGAKClRoZSBzaGFkZWQgYXJlYSBhcm91bmQgdGhlIGxpbmUgaXMgdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIGFyb3VuZCB0aGUgbGluZS4gV2UgY2FuIHN3aXRjaCB0aGlzIG9mZiBieSAgYWRkaW5nIGBzZSA9IEZgICh3aGljaCBpcyBzaG9ydCBmb3IgJ3N0YW5kYXJkIGVycm9yID0gRmFsc2UnKQoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpNeV9ncmFwaCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKHNlID0gRikKYGBgCgoKV2hhdCBpZiB3ZSB3YW50IG91ciBsaW5lIHRvIGJlIGEgZGlyZWN0IGxpbmU/CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZSA9IEYsIG1ldGhvZCA9IGxtKQoKYGBgCkhvdyB0byBjaGFuZ2UgdGhlIGxhYmVscyBvZiB4IGFuZCB5IGF4ZXM/CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZSA9IEYsIG1ldGhvZCA9IGxtKSArCiAgbGFicyh4ID0gIkV4YW0gc2NvcmVzICUiLCB5ID0gIkFueGlldHkgc2NvcmVzIikKYGBgCgpIaXN0b2dyYW1zIGFyZSB1c2VkIHRvIHNob3cgZGlzdHJpYnV0aW9ucyBvZiB2YXJpYWJsZXMgd2hpbGUgYmFyIGNoYXJ0cyBhcmUgdXNlZCB0byBjb21wYXJlIHZhcmlhYmxlcy4gSGlzdG9ncmFtcyBwbG90IHF1YW50aXRhdGl2ZSBkYXRhIHdpdGggcmFuZ2VzIG9mIHRoZSBkYXRhIGdyb3VwZWQgaW50byBiaW5zIG9yIGludGVydmFscyB3aGlsZSBiYXIgY2hhcnRzIHBsb3QgY2F0ZWdvcmljYWwgZGF0YS4KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KI2dncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEFueGlldHksIHkgPSBFeGFtICkpICsgZ2VvbV9oaXN0b2dyYW0oKQojIHRoZSBjb2RlIGFib3ZlIGdpdmVzIGFuIGVycm9yIGFzIGdlb21faGlzdG9ncmFtIGNhbiBvbmx5IGhhdmUgeCBvciB5IGF4aXMgaW4gaXRzIGFlcygpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHkgPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzEpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzEsIGZpbGwgPSAiZ3JlZW4iKQoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gQW54aWV0eSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMxLCBmaWxsID0gImdyZWVuIiwgY29sID0gInJlZCIpCmBgYAoKTGV0J3Mgc3RvcCB1c2luZyB0aGUgTXlfZ3JhcGggdmFyaWFibGUgYW5kIHdyaXRlIHRoZSB3aG9sZSBjb2RlIGZyb20gdGhlIHN0YXJ0IGFnYWluIGZvciBhIGJhciBjaGFydApgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CmdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IFNsZWVwX3F1YWxpdHkpKSsKICBnZW9tX2JhcigpCmBgYApCZWNhdXNlIHdlIHdhbnQgdG8gcGxvdCBhIHN1bW1hcnkgb2YgdGhlIGRhdGEgKHRoZSBtZWFuKSByYXRoZXIgdGhhbiB0aGUgcmF3IHNjb3JlcyB0aGVtc2VsdmVzLCB3ZSBoYXZlIHRvIHVzZSBhIHN0YXQuCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIpCgoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIsIHBvc2l0aW9uID0gImRvZGdlIikKYGBgCgpUaGUgb3RoZXIgd2F5IHRvIGdldCB0aGUgc2FtZSBwbG90IHRoYXQgdGhlIGNvZGUgYWJvdmUgZ2l2ZXMsIGlzIHVzaW5nIHRoZSBzdGF0X3N1bW1hcnkgZnVuY3Rpb24gdGhhdCB0YWtlcyB0aGUgZm9sbG93aW5nIGdlbmVyYWwgZm9ybTogYHN0YXRfc3VtbWFyeShmdW5jdGlvbiA9IHgsIGdlb20gPSB5KWAKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCmBgYAoKSG93IHRvIGNvbWJpbmUgbXVsdGlwbGUgcGxvdHM/IEhvdyB0byBjb21iaW5lIG11bHRpcGxlIHBsb3RzPyBXZSBjYW4gdXNlIHRoZSBgcGF0Y2h3b3JrYCBwYWNrYWdlLiBBIG5pY2UgdHV0b3JpYWwgb24gdXNpbmcgdGhpcyBwYWNrYWdlIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9wYXRjaHdvcmsuZGF0YS1pbWFnaW5pc3QuY29tL2FydGljbGVzL3BhdGNod29yay5odG1sKQoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3BhdGNod29ya18xLmpwZycpKQpgYGAKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0KcDEgPSBNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aCgpCgpwMiA9IGdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEFueGlldHkpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMSkKCnAzID0gZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCgpwNCA9IE15X2dyYXBoICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGLCBtZXRob2QgPSBsbSkgKwogIGxhYnMoeCA9ICJFeGFtIHNjb3JlcyAlIiwgeSA9ICJBbnhpZXR5IHNjb3JlcyIpCgpjb21iaW5lZCA9IHAxICsgcDIrIHAzICsgcDQgKyBwbG90X2xheW91dChucm93ID0gNCwgYnlyb3cgPSBGKQoKY29tYmluZWQKCnAxIHwgcDIgLyBwMyAvIHA0CgpwMSB8IHAyIC8gKHAzIC8gcDQpCmBgYAoKCmBnZ3NhdmUoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIGEgdmVyc2F0aWxlIGV4cG9ydGluZyBmdW5jdGlvbiB0aGF0IGNhbiBleHBvcnQgYXMgUG9zdFNjcmlwdCAoLmVwcy8ucHMpLCB0ZXggKHBpY3RleCksIHBkZiwganBlZywgdGlmZiwgcG5nLCBibXAsIHN2ZyBhbmQgd21mIChpbiBXaW5kb3dzIG9ubHkpLiBJbiBpdHMgYmFzaWMgZm9ybSwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZnVuY3Rpb24gaXMgdmVyeSBzaW1wbGU6IGBnZ3NhdmUoZmlsZW5hbWUpYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD01fQpnZ3NhdmUoY29tYmluZWQsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsICJjb21iaW5lZC5wbmciKSwgZHBpPTMwMCkKYGBgCgoKTm93IHRoYXQgd2UgbGVhcm5lZCB0aGUgYmFzaWNzIG9mIGdncGxvdCwgbGV0J3MgZHJhdyBzb21lIHBsb3QgZm9yIG91ciBleHBlcmltZW50IGRhdGEuIEZpcnN0LCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIGRhdGFzZXQgd2l0aCBhZ2dyZWdhdGVkIGBkZXByZXNzaW9uX3Njb3JlYCBzY29yZXMgb3ZlciBgZ3JvdXBgIGFuZCBgc3RhZ2VgLiBXZSB3aWxsIHVzZSB0aGlzIGRhdGFzZXQgZm9yIGxpbmUgYW5kIGJhciBncmFwaHMuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CmxpYnJhcnkoZ2dzY2kpCgpkYXRhX2V4cDFfb3JpZyA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJjbGVhbmVkX2RhdGFfZXhwMS5jc3YiKSkKCmRhdGFfZXhwMSA8LSBkYXRhX2V4cDFfb3JpZyU+JSAKICAjbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgZmFjdG9yKSAlPiUKICBtdXRhdGUoc3ViamVjdD0gZmFjdG9yKHN1YmplY3QpLCAjIGNvbnZlcnQgYWxsIGNoYXJhY3RlcnMgdG8gZmFjdG9yCiAgICAgICAgIGdyb3VwID0gZmFjdG9yKGdyb3VwKSwKICAgICAgICAgc3RhZ2UgPSBmYWN0b3Ioc3RhZ2UpKQoKCmFnZ3JlZ2F0ZWRfZGF0YV9leHAxIDwtIGRhdGFfZXhwMSAlPiUKICBncm91cF9ieShzdGFnZSwgZ3JvdXApICU+JQogIG11dGF0ZShkZXByZXNzaW9uX3Njb3JlID0gbWVhbihkZXByZXNzaW9uX3Njb3JlKSkgJT4lCiAgdW5ncm91cCgpCgoKYmFycGxvdF9leHAxIDwtIGFnZ3JlZ2F0ZWRfZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1zdGFnZSwgeT0gZGVwcmVzc2lvbl9zY29yZSwgZmlsbD1ncm91cCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249ICJkb2RnZSIpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfYncoKSArIAogIHNjYWxlX2ZpbGxfamFtYSgpIAoKI2dnc2F2ZShiYXJwbG90X2V4cDEsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsImJhcnBsb3RfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgpiYXJwbG90X2ZhY2V0X2V4cDEgPC0gYWdncmVnYXRlZF9kYXRhX2V4cDEgJT4lCiAgZ2dwbG90KGFlcyh4PWdyb3VwLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBmaWxsPXN0YWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbj0gImRvZGdlIikrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgZmFjZXRfd3JhcCh+c3RhZ2UsIG5yb3cgPSAxKSsKICBzY2FsZV9maWxsX2pjbygpIAoKI2dnc2F2ZShiYXJwbG90X2ZhY2V0X2V4cDEsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsImJhcnBsb3RfZmFjZXRfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgpsaW5lcGxvdF9leHAxIDwtIGFnZ3JlZ2F0ZWRfZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3Ioc3RhZ2UpLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBncm91cD0gZ3JvdXAsIGNvbG9yPSBncm91cCkpICsKICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlPSBncm91cCkpICsKICBnZW9tX3BvaW50KHNpemU9IDUpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkgKwogIHNjYWxlX2NvbG9yX25lam0oKSAKCiNnZ3NhdmUobGluZXBsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwibGluZXBsb3RfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgp2aW9saW5wbG90X2V4cDEgPC0gZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3Ioc3RhZ2UpLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBmaWxsPSBncm91cCkpICsKICBnZW9tX3Zpb2xpbigpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSkpICsKICBzY2FsZV9maWxsX2QzKCkgCgojZ2dzYXZlKHZpb2xpbnBsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwidmlvbGlucGxvdF9leHAxLnBuZyIpLCBkcGk9MzAwKQoKCmJveHBsb3RfZXhwMSA8LSBkYXRhX2V4cDEgJT4lCiAgZ2dwbG90KGFlcyh4PWZhY3RvcihzdGFnZSksIHk9IGRlcHJlc3Npb25fc2NvcmUsIGZpbGw9IGdyb3VwKSkgKwogIGdlb21fYm94cGxvdCgpKwogICNnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGg9MC43NSksIGFscGhhPSAuNSkrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSkgKwogIHNjYWxlX2ZpbGxfc2ltcHNvbnMoKSAKCiNnZ3NhdmUoYm94cGxvdF9leHAxLCBmaWxlbmFtZSA9IGhlcmUoIm91dHB1dHMiLCJib3hwbG90X2V4cDEucG5nIiksIGRwaT0zMDApCgoKYm94cGxvdF9mYWNldF9leHAxIDwtIGRhdGFfZXhwMSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZmFjdG9yKHN0YWdlKSwgeT0gZGVwcmVzc2lvbl9zY29yZSwgZmlsbD0gZ3JvdXApKSArCiAgZ2VvbV9ib3hwbG90KCkrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpICsKICBmYWNldF93cmFwKH5ncm91cCkrCiAgc2NhbGVfY29sb3Jfc2ltcHNvbnMoKSAKCiNnZ3NhdmUoYm94cGxvdF9mYWNldF9leHAxLCBmaWxlbmFtZSA9IGhlcmUoIm91dHB1dHMiLCJib3hwbG90X2ZhY2V0X2V4cDEucG5nIiksIGRwaT0zMDApCgpgYGAKCkxldCdzIGNvbWJpbmUgb3VyIHBsb3RzOgoKYGBge3IgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9Cgpjb21iaW5lZF9wbG90X2V4cDEgPC0gYmFycGxvdF9mYWNldF9leHAxIC8gKGxpbmVwbG90X2V4cDErdmlvbGlucGxvdF9leHAxK2JveHBsb3RfZXhwMSkKY29tYmluZWRfcGxvdF9leHAxCmBgYAoKQW5kIGhlcmUsIHdlIHNhdmUgb3VyIHBsb3RzIHRvIHRoZSBgb3V0cHV0c2AgZm9sZGVyLgpgYGB7cm1lc3NhZ2U9Rn0KZ2dzYXZlKGNvbWJpbmVkX3Bsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwiY29tYmluZWRfcGxvdF9leHAxLnBuZyIpLCBkcGk9MzAwLCB3aWR0aCA9IDEyKQpgYGAKCgoKIyBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdub3Rfbm9ybWFsLnBuZycpKQpgYGAKCk5vdywgbGV0J3MgZG8gc29tZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLiBOb3csIHdlIGNhbiBvcGVuIGEgbmV3IHNjcmlwdCBjYWxsZWQgYGRhdGFfYW5hbHlzaXMucmAgYW5kIHJlYWQgc29tZSBkYXRhc2V0cy4gVGhlbiB3ZSB1c2UgYHNraW1yYCBwYWNrYWdlIHRvIGRlc2NyaWJlIG91ciBkYXRhLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsfQpuYXJjaXNzaXNtX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwibmFyY2lzc2lzbV9kYXRhLmNzdiIpKQpuYXJjaXNzaXNtX2RhdGEgJT4lIHNraW1yOjpza2ltKCkKYGBgCgoqICpFeGVyY2lzZSo6IE9wZW4gdGhlIGRhdGFzZXQgY2FsbGVkIGB0cmVhdG1lbnRfZGF0YS5jc3ZgIGFuZCBkbyBhIGRlc2NyaXB0aXZlIGFuYWx5c2lzOgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KdHJlYXRtZW50X2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidHJlYXRtZW50X2RhdGEuY3N2IikpCnRyZWF0bWVudF9kYXRhICU+JSBza2ltcjo6c2tpbSgpCmBgYAoKKiAqRXhlcmNpc2UqOiBEbyB0aGUgc2FtZSB0aGluZyBmb3IgdGhlIGBtZW1vcnlfZGF0YS5jc3ZgLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9Cm1lbW9yeV9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsIm1lbW9yeV9kYXRhLmNzdiIpKQptZW1vcnlfZGF0YSAlPiUgZ3JvdXBfYnkodGltZSkgJT4lCiAgc2tpbXI6OnNraW0oKQpgYGAKCk5vdywgbGV0J3MgZGVzY3JpYmUgb3VyIGV4cGVyaW1lbnQgZGF0YToKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLH0KZGF0YV9leHAxX29yaWcgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwiY2xlYW5lZF9kYXRhX2V4cDEuY3N2IikpCmBgYAoKSG93IG1hbnkgcGFydGljaXBhbnRzIGluIHRvdGFsPwoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmRhdGFfZXhwMSAlPiUgc3VtbWFyaXNlKG49IG5fZGlzdGluY3Qoc3ViamVjdCkpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBlY2hvPUYsIGV2YWw9Rn0KZGF0YV9leHAxICU+JSBzdW1tYXJpc2Uobj0gbl9kaXN0aW5jdChzdWJqZWN0KSklPiUKICBrbml0cjo6a2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZnVsbF93aWR0aCA9IEYpCmBgYAoKSG93IG1hbnkgcGFydGljaXBhbnRzIGRvIHdlIGhhdmUgaW4gZWFjaCBncm91cD8KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KZGF0YV9leHAxICU+JSAKICBncm91cF9ieShzdWJqZWN0KSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoKT09MSkgJT4lIAogIHVuZ3JvdXAgKCkgJT4lIAogIGdyb3VwX2J5KGdyb3VwKSAlPiUgCiAgY291bnQoKSAKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZWNobz1GfQpkYXRhX2V4cDEgJT4lIGdyb3VwX2J5KHN1YmplY3QpICU+JSBmaWx0ZXIocm93X251bWJlcigpPT0xKSAlPiUgdW5ncm91cCAoKSAlPiUgZ3JvdXBfYnkoZ3JvdXApICU+JSBjb3VudCgpICU+JQogIGtuaXRyOjprYWJsZSgpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQpCmBgYAoKRmluZCB0aGUgbWVhbiBhbmQgc2QgZm9yIG51bWVyaWMgdmFyaWFibGVzIHVzaW5nIGJhc2UgUiBgc3VtbWFyeWAgZnVuY3Rpb246CgpgYGB7cn0KZGF0YV9leHAxICU+JSAKICBncm91cF9ieShzdWJqZWN0KSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoKT09MSkgJT4lIAogIHVuZ3JvdXAgKCkgJT4lCiAgc3VtbWFyeSgpCmBgYAoKQWx0ZXJuYXRpdmVseSwgd2UgY2FuIHVzZSBgc2tpbXJgIGxpYnJhcnk6CmBgYHtyIGV2YWw9Rn0KZGF0YV9leHAxICU+JSAKICBncm91cF9ieShzdWJqZWN0KSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoKT09MSkgJT4lIAogIHVuZ3JvdXAgKCkgJT4lIAogIGRwbHlyOjpzZWxlY3QgKGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pICU+JSAKICBza2ltcjo6c2tpbSgpCmBgYAoKYGBge3IgZWNobz1GfQpkYXRhX2V4cDEgJT4lIAogIGdyb3VwX2J5KHN1YmplY3QpICU+JSAKICBmaWx0ZXIocm93X251bWJlcigpPT0xKSAlPiUgCiAgdW5ncm91cCAoKSAlPiUgCiAgZHBseXI6OnNlbGVjdCAoYWdlLCBkZXByZXNzaW9uX3Njb3JlLCBhbnhpZXR5X3RvdGFsLCBzbGVlcF9xdWFsaXR5LCBsaWZlX3NhdGlzZmFjdGlvbikgJT4lIAogIHNraW1yOjpza2ltKCkgJT4lCiAga25pdHI6OmthYmxlKCkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IEYpJT4lCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICI3ODBweCIpCmBgYAoKCiogKkV4ZXJjaXNlKjogRm9yIHRoaXMgZXhlcmNpc2UsIHdlIHVzZSBhIGRhdGFzZXQgb2Ygb25lIG9mIG15IG93biBzdHVkaWVzLiBJbiB0aGlzIHN0dWR5LCB3ZSBhc2tlZCBwYXJ0aWNpcGFudHMgdG8gZ3Vlc3MgdGhlIHBoeXNpY2FsIGJyaWdodG5lc3Mgb2YgcmVhc29uaW5nIGFyZ3VtZW50cyBhbmQgdGhlbiB3ZSBnYXZlIGEgY29nbml0aXZlIGFiaWxpdHkgdGVzdC4gKFNlZSB0aGUgb3JpZ2luYWwgc3R1ZHkgW2hlcmVdKGh0dHBzOi8vb3NmLmlvL2VieG5mLykpLiBPcGVuIGBnaGFzZW1pX2JyaWdodG5lc3NfZXhwNC5jc3ZgIGZpbGUgYW5kIGFuc3dlciB0byB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoKCjEuIEhvdyBtYW55IHBhcnRpY2lwYW50cyBkaWQgd2UgdGVzdCBpbiB0b3RhbD8KMi4gRmluZCBvdXQgaG93IG1hbnkgbWFsZSBhbmQgZmVtYWxlIHdlIHRlc3RlZC4KMy4gQ2FsY3VsYXRlIG1lYW4gYW5kIHNkIGZvciBhZ2UgYW5kIGNvZ25pdGl2ZSBhYmlsaXR5IChgY29nX2FiaWxpdHlgKS4KCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KZ2hhc2VtaV9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsImdoYXNlbWlfYnJpZ2h0bmVzc19leHA0LmNzdiIpKQoKZ2hhc2VtaV9kYXRhICU+JSBzdW1tYXJpc2UobiA9IG5fZGlzdGluY3QocGFydGljaXBhbnQpKSAjIG51bWJlciBvZiBwYXJ0aWNpcGFudHM6MjAwCgpnaGFzZW1pX2RhdGEgJT4lIGdyb3VwX2J5IChwYXJ0aWNpcGFudCkgJT4lIGZpbHRlciAocm93X251bWJlcigpPT0xKSAlPiUgZ3JvdXBfYnkgKGdlbmRlcikgJT4lIHN1bW1hcmlzZShuPSBuKCkpICU+JSB1bmdyb3VwKCkgIyAxODMgZmVtYWxlLCAxNyBtYWxlCgpnaGFzZW1pX2RhdGEgJT4lIGRwbHlyOjpzZWxlY3QgKGFnZSwgY29nX2FiaWxpdHkpICU+JSBza2ltcjo6c2tpbSgpICMgbWVhbiBhbmQgc2QgZm9yIGFnZSBhbmQgY29nbml0aXZlIGFiaWxpdHkKYGBgCgoKCiMgRGF0YSBBbmFseXNpcwoKIyMgdC10ZXN0CgpOb3csIHdlIHVzZSB0aGUgdHJlYXRtZW50IGRhdGEgdG8gcnVuIHRocmVlIGRpZmZlcmVudCBpbmRlcGVuZGVudCB0LXRlc3RzLiBTdXBwb3NlIHdlIGRpZCBhbiBleHBlcmltZW50IHRvIGNvbXBhcmUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgQ0JUIHZzLiBQc3ljaG9keW5hbWljIHRoZXJhcGllcyBpbiBkZWNyZWFzaW5nIGFueGlldHksIGFuZCBkZXByZXNzaW9uIGFuZCBhbHNvIGluIGltcHJvdmluZyBsaWZlIHNhdGlzZmFjdGlvbjoKCmBgYHtyfQojIHQudGVzdCAoaW5kZXApCnQudGVzdChhbnhpZXR5fnRyZWF0bWVudCwgZGF0YT0gdHJlYXRtZW50X2RhdGEpCnQudGVzdChkZXByZXNzaW9ufnRyZWF0bWVudCwgZGF0YT0gdHJlYXRtZW50X2RhdGEpCnQudGVzdChsaWZlX3NhdGlzZmFjdGlvbn50cmVhdG1lbnQsIGRhdGE9IHRyZWF0bWVudF9kYXRhKQpgYGAKCkluIGFub3RoZXIgZXhwZXJpbWVudCwgc3VwcG9zZSB3ZSBoYXZlIGNyZWF0ZWQgYSBtZXRob2QgdG8gYm9vc3QgbWVtb3J5LiBUaGVuLCB3ZSByZWNydWl0IHNvbWUgcGFydGljaXBhbnRzLCBkbyBhIG1lbW9yeSBwcmUtdGVzdCwgaW1wbGVtZW50IHRoZSBtZXRob2QsIGFuZCBkbyBhIG1lbW9yeSBwb3N0LXRlc3QsIE5vdywgd2Ugd2FudCB0byBzZWUgd2hldGhlciBvdXIgbWV0aG9kIGhhdmUgaW1wcm92ZWQgcGFydGljaXBhbnRzJyBtZW1vcnk6IAoKYGBge3J9CiMgdC50ZXN0IChwYWlyZWQpCnQudGVzdChtZW1vcnlfc2NvcmV+dGltZSwgZGF0YT0gbWVtb3J5X2RhdGEsIHBhaXJlZD0gVCkKYGBgCgpOb3cgdGhhdCB3ZSBsZWFybmVkIGFib3V0IHQtdGVzdCwgbGV0J3MgcGVyZm9ybSB0aGlzIHRlc3Qgb24gb3VyIGRhdGFzZXQuIElzIHRoZXJlIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIGdyb3VwcyBhdCB0aGUgZmlyc3Qgc3RhZ2U/IElkZWFsbHksIHdlIHdhbnQgcGFydGljaXBhbnRzJyBkZXByZXNpb24gc2NvcmUgYXQgdGhlIGZpcnN0IHN0YWdlIHRvIGJlIHNpbWlsYXIgZm9yIGJvdGggZ3JvdXBzIGJlY2F1c2Ugd2UgaGF2ZSBub3Qgc3RhcnRlZCBvdXIgdHJlYXRtZW50IHlldC4gUHJldmlvdXMgZ3JhcGhzIHNob3dlZCB1cyB0aGF0IGRlcHJlc3Npb24gc2NvcmVzIG9mIHRoZSBDQlQgYW5kIFBzeWNob2R5bmFtaWMgZ3JvdXBzIGF0IHRoaXMgc3RhZ2UgYXJlIHByZXR0eSBjbG9zZS4gTGV0J3MgdGVzdCB0aGF0IHVzaW5nIGFuICoqaW5kZXBlbmRlbnQgdC10ZXN0KiogKGJlY2F1c2Ugd2UgaGF2ZSAyIGluZGVwZW5kZW50IGdyb3Vwcyk6CgpgYGB7cn0KIyBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMgYXQgdGhlIGZpcnN0IHN0YWdlPwpkYXRhX2V4cDEgJT4lIAogIGdyb3VwX2J5KGdyb3VwKSAlPiUgCiAgZmlsdGVyKHN0YWdlPT0nc3RhZ2UxJykgJT4lIAogIHVuZ3JvdXAgKCkgJT4lCiAgdC50ZXN0KGRlcHJlc3Npb25fc2NvcmV+Z3JvdXAsIGRhdGEgPSAuLCBwYWlyZWQ9RkFMU0UpCmBgYAoKTm93LCB3ZSB3b25kZXIgaWYgcHN5Y2hvdGhlcmFweSB0cmVhdG1lbnRzIHdlcmUgZWZmZWN0aXZlIGF0IGFsbCwgcmVnYXJkbGVzcyBvZiB0aGUgdHJlYXRtZW50IG1ldGhvZC4gU28sIHdlIHdvdWxkIGxpa2UgdG8gdGVzdCBpZiBkZXByZXNpb24gc2NvcmUgYXQgdGhlIGZvcnRoIHN0YWdlIGFyZSBsb3dlciB0aGFuIHNjb3JlcyBhdCB0aGUgc3RhZ2UgMj8gU2luY2UgYSBwYWlyIG9mIHNjb3JlIGF0IHN0YWdlIDIgYW5kIHN0YWdlIDQgaXMgY29taW5nIGZyb20gYSBzYW1lIHBlcnNvbiwgd2UgdXNlICoqcGFpcmVkIHQtdGVzdCoqLgoKYGBge3J9CiMgSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGJldHdlZW4gcmF0aW5ncyBvZiBzdGFnZTIgYW5kIHN0YWdlND8KZGF0YV9leHAxICU+JSAKICBmaWx0ZXIoc3RhZ2U9PSdzdGFnZTInIHwgc3RhZ2U9PSdzdGFnZTQnKSAlPiUgCiAgdW5ncm91cCAoKSAlPiUKICB0LnRlc3QoZGVwcmVzc2lvbl9zY29yZX5zdGFnZSwgZGF0YSA9IC4sIHBhaXJlZD1UUlVFKQpgYGAKCgoqICpFeGVyY2lzZSo6IEpvaG4gZXQgYWwuICgyMDE5KSBpbnZlc3RpZ2F0ZWQgdGhlIGNvbnNlcXVlbmNlcyBvZiBiYWNraW5nIGRvd24gKGNoYW5naW5nIG9uZSdzIG1pbmQgaW4gbGlnaHRzIG9mIGV2aWRlbmNlKWFuZCBob3cgb3RoZXIgcGVvcGxlIHZpZXcgc29tZW9uZSB3aG8gY2hhbmdlIHRoZWlyIG1pbmQuIEluIHRoZWlyIHNlY29uZCBleHBlcmltZW50cywgdGhleSBwcmVzZW50ZWQgcGFydGljaXBhbnRzIGVpdGhlciB3aXRoIGEgcGVyc29uIHdobyBjaGFuZ2VzIHRoZWlyIG1pbmQgb3IgYSBwZXJzb24gd2hvIHJlZnVzZXMgdG8gYmFjayBkb3duLiBUaGVuLCB0aGV5IGFza2VkIHBhcnRpY2lwYW50cyB0byByYXRlIGhvdyBpbnRlbGxpZ2VudCBhbmQgY29uZmlkZW50IHRoZSBwZXJzb24gaXMgKFNlZSB0aGUgb3JpZ2luYWwgc3R1ZHkgW2hlcmVdKGh0dHBzOi8vd3d3Lmhicy5lZHUvZmFjdWx0eS9QdWJsaWNhdGlvbiUyMEZpbGVzL0pvaG4lMjBldCUyMGFsJTIwLSUyMHNlbGYtcHJlc2VudGF0aW9uYWwlMjBjb25zZXF1ZW5jZXNfYjg1YjJjNDMtYTViNS00NzRjLTllMmMtZTk4NTNiMTA3MjdlLnBkZikpLiBUaGV5IHJlcG9ydGVkIHRoYXQ6IAoKPiAiUmVsYXRpdmUgdG8gdGhlIGVudHJlcHJlbmV1ciB3aG8gZGlkIG5vdCBiYWNrIGRvd24sIHBhcnRpY2lwYW50cyBqdWRnZWQgdGhlIGVudHJlcHJlbmV1ciB3aG8gYmFja2VkIGRvd24gYXMgbW9yZSBpbnRlbGxpZ2VudCAoTV9iYWNrZWRfZG93bj01LjEzIG91dCBvZiA3LCBTRD0xLjA5OyBNX2RpZF9ub3RfYmFja19kb3duPTMuOTcsIFNEPTEuNTQ7IHQoMjcxLjEyKT3iiJI3LjU5LCBwIDwgLjAwMSkgYnV0IGxlc3MgY29uZmlkZW50IChNX2JhY2tlZF9kb3duPTQuNTAgb3V0IG9mIDcsIFNEPTEuMzY7IE1fZGlkX25vdF9iYWNrX2Rvd249NS42NSwgU0Q9MS4xMDsgdCgyOTEuMDEpPTguMDgsIHAgPCAuMDAxKS4iLgoKT3BlbiB0aGUgYGpvaG5fYmFja2Rvd25fZXhwMi5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cy4gUnVuIHR3byBzZXBhcmF0ZSBpbmRlcGVuZGVudCB0LXRlc3QsIG9uZSB3aXRoIGBpbnRlbGxpZ2VudGAgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgb25lIHdpdGggYGNvbmZpZGVudGAgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4gRm9yIGJvdGggdC10ZXN0LCB1c2UgYGJhY2tfZG93bmAgYXMgdGhlIGJldHdlZW4tc3ViamVjdCBpbmRlcGVuZGVudCB2YXJpYWJsZS4KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpqb2huX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwiam9obl9iYWNrZG93bl9leHAyLmNzdiIpKQoKCnQudGVzdChpbnRlbGxpZ2VudH5iYWNrX2Rvd24sIGRhdGEgPSBqb2huX2RhdGEsIHBhaXJlZD1GQUxTRSkKdC50ZXN0KGNvbmZpZGVudH5iYWNrX2Rvd24sIGRhdGEgPSBqb2huX2RhdGEsIHBhaXJlZD1GQUxTRSkKYGBgCgoKIyMgQW5hbHlzaXMgb2YgVmFyaWFuY2UgKEFOT1ZBKQoKTm93LCBsZXQncyBhbmFseXNpcyBvdXIgbWFpbiBleHBlcmltZW50IGRhdGE6IERvIHBhcnRpY2lwYW50cyBpbiB0aGUgQ0JUIGdyb3VwIHNob3cgYmV0dGVyIG91dGNvbWUgY29tcGFyZWQgdG8gcGFydGljaXBhbnRzIGluIHRoZSBQc3ljaG9keW5hbWljIGdyb3VwPyBTdXBwb3NlIHdlIGJlbGlldmUgdGhhdCBwYXJ0aWNpcGFudHMgc2hvdWxkIHNob3cgbG93ZXIgZGVwcmVzc2lvbiBhZnRlciA1IG9yIDEwIHNlc3Npb25zIG9mIGJvdGggcHN5Y2hvdGhlcmFweSB0cmVhdG1lbnRzIGFuZCB0aGlzIGRlY3JlYXNlIHNob3VsZCBiZSBtb3JlIHByb25vdW5jZWQgZm9yIENCVCB0aGFuIHBzeWNob2R5bmFtaWMgcHN5Y2hvdGhlcmFweS4gSWYgdGhpcyBpcyB0aGUgY2FzZS4gd2UgZXhwZWN0IGFuIGludGVyYWN0aW9uIGluIHRoZSB0cmFkaXRpb25hbCAqKkFuYWx5c2lzIG9mIFZhcmlhbmNlIChBT05WQSkqKiB0ZXN0LgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmFvdl9tMSA8LSBhb3ZfY2FyIChkZXByZXNzaW9uX3Njb3JlIH4gZ3JvdXAqc3RhZ2UgKwogICAgICAgICAgICAgICAgICAgICBFcnJvcihzdWJqZWN0L3N0YWdlKSwgZGF0YSA9IGRhdGFfZXhwMSkgIApgYGAKCmBgYHtyIGVjaG89Rn0Ka25pdHI6OmthYmxlKG5pY2UoYW92X20xKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCmBgYAoKQXMgeW91IGNhbiBzZWUsIHdlIGZvdW5kIGEgc2lnbmlmaWNhbnQgbWFpbiBlZmZlY3Qgb2Ygc3RhZ2UgYW5kIGEgc2lnbmlmaWNhbnQgZ3JvdXAgYnkgc3RhZ2UgaW50ZXJhY3Rpb24uIFdlIGNhbiB1c2UgdGhlIGBlbW1lYW5zYCBwYWNrYWdlIHRvIGRvIHBvc3QtaG9jIHRlc3RzLgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CiMgbWFpbiBlZmZlY3Qgb2Ygc3RhZ2UKZW1tZWFucyhhb3ZfbTEsICdzdGFnZScpCnBhaXJzKGVtbWVhbnMoYW92X20xLCAnc3RhZ2UnKSwgYWRqdXN0PSAnaG9sbScpCmBgYAoKCmBgYHtyIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIGdyb3VwIGJ5IHN0YWdlIGludGVyYWN0aW9uCmVtbWVhbnMoYW92X20xLCAiZ3JvdXAiLCBieT0gInN0YWdlIikKdXBkYXRlKHBhaXJzKGVtbWVhbnMoYW92X20xLCAiZ3JvdXAiLCBieT0gInN0YWdlIikpLCBieSA9IE5VTEwsIGFkanVzdCA9ICJob2xtIikgCmBgYAoKWW91IGNhbiB1c2UgdGhlIGBhZmV4X3Bsb3RgIGZ1bmN0aW9uIGZyb20gYWZleCB0byBjcmVhdGUgYmVhdXRpZnVsIHBsb3RzLiBUaG9zZSBwbG90cyBpbnRlcmFjdHMgbmljZWx5IHdpdGggZ2dwbG90OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDB9CmFmZXhfcGxvdChhb3ZfbTEsIHggPSAic3RhZ2UiLCB0cmFjZSA9ICJncm91cCIsIGVycm9yPSdiZXR3ZWVuJywKICAgICAgICAgIGxpbmVfYXJnID0gbGlzdChzaXplPTEpLAogICAgICAgICAgcG9pbnRfYXJnID0gbGlzdChzaXplPTMuNSksCiAgICAgICAgICBkYXRhX2FyZyA9IGxpc3Qoc2l6ZT0gMSwgY29sb3I9ICdncmV5Jywgd2lkdGg9LjQpLAogICAgICAgICAgZGF0YV9nZW9tID0gZ2VvbV9ib3hwbG90LAogICAgICAgICAgbWFwcGluZyA9IGMoImxpbmV0eXBlIiwgInNoYXBlIiwgImZpbGwiKSwKICAgICAgICAgIGxlZ2VuZF90aXRsZSA9ICJHcm91cCIpICsKICBsYWJzKHkgPSAiRGVwcmVzc2lvbiBTY29yZSIsIHggPSAiIikgKwogIHRoZW1lX2J3KCkrICMgcmVtb3ZlIHRoZSBncmV5IGJhY2tncm91bmQgYW5kIGdyaWQKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTMpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMyksCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwKICAgICAgICBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgxLCAiY20iKSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAnYmxhY2snLCBmaWxsID0gJ3doaXRlJywgbGluZXR5cGU9J3NvbGlkJykpKwogIHNjYWxlX2NvbG9yX3NpbXBzb25zKCkgKwogIHNjYWxlX2ZpbGxfc2ltcHNvbnMoKQpgYGAKCgpJZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gdGhpcyB0b3BpYywgY2hlY2sgb3V0IHRoaXMgbmljZSB0dXRvcmlhbCBhYm91dCBbdXNpbmcgYWZleCB0byBydW4gQU5PVkFdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9hZmV4L3ZpZ25ldHRlcy9hZmV4X2Fub3ZhX2V4YW1wbGUuaHRtbCksIGFuZCBhbHNvIHRoaXMgaW50ZXJlc3RpbmcgdHV0b3JpYWwgb24gdGhlIFtlbW1lYW5zIHBhY2thZ2VdKGh0dHBzOi8vYW9zbWl0aC5yYmluZC5pby8yMDE5LzAzLzI1L2dldHRpbmctc3RhcnRlZC13aXRoLWVtbWVhbnMvKS4KCiogKkV4ZXJjaXNlKjogUm90ZWxsbyBldCBhbC4gKDIwMTgpIGludmVzdGlnYXRlZCB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgcmFjZSAoV2hpdGUgdnMuIEJsYWNrIGZhY2VzKSBhbmQgdGhlIGd1bi10b29sIGp1ZGdtZW50cy4gSW4gdGhlaXIgZmlyc3QgZXhwZXJpbWVudHMsIHRoZXkgcHJlc2VudGVkIHBhcnRpY2lwYW50cyB3aXRoIDE2IFdoaXRlIG1hbGUgZmFjZXMgYW5kIDE2IEJsYWNrIG1hbGUgZmFjZXMsIGFuZCBmb2xsb3dpbmcgdGhhdCA4IGltYWdlcyBvZiBndW5zIGFuZCA4IGltYWdlcyBvZiB0b29scy4gVGhleSBhc2tlZCBwYXJ0aWNpcGFudHMgdG8ganVkZ2UgaWYgdGhlIG9iamVjdCBpcyBhIHRvb2wgb3IgYSBndW4gYnkgcHJlc3Npbmcga2V5Ym9hcmQgYnV0dG9ucy4gVGhlbiwgdGhleSByYW4gYW4gQU5PVkEgdG8gc2VlIGlmIHBhcnRpY2lwYW50cycgZ3VuIHJlc3BvbnNlcyBhcmUgaGlnaGVyIGZvciBhbnkgb2YgdGhlIHJhY2VzLiBTbywgdGhleSBpbmNsdWRlZCBwcmltZSByYWNlIChCbGFjaywgV2hpdGUpIGFuZCB0YXJnZXQgaWRlbnRpdHkgKGd1biwgdG9vbCkgYXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCBwYXJ0aWNpcGFudHMnIGd1biByZXNwb25zZXMgYXMgZGVwZW5kZW50IHZhcmlhYmxlIGludG8gdGhlaXIgbGluZWFyIG1vZGVsIChTZWUgdGhlIG9yaWdpbmFsIHN0dWR5IFtoZXJlXShodHRwczovL29ubGluZS51Y3ByZXNzLmVkdS9jb2xsYWJyYS9hcnRpY2xlLzQvMS8zMi8xMTI5ODYvVGhlLVNoYXBlLW9mLVJPQy1DdXJ2ZXMtaW4tU2hvb3Rlci1UYXNrcykpLiBUaGV5IGZvdW5kIHRoYXQ6IAoKPiAiUGFydGljaXBhbnRzIG1hZGUgbW9yZSBndW4gcmVzcG9uc2VzIHRvIGd1bnMgdGhhbiB0byB0b29scywgRigxLDQ1KSA9IDUzMjQzLCBwIDwgMC4wMDAxLCDOtzJnID0gMC45OTguIEhvd2V2ZXIsIHRoZSByYWNlIG9mIHRoZSBwcmltZSBmYWNlIGRpZCBub3QgbWF0dGVyLCBGKDEsNDUpID0gMC4yODcsIHAgPiAwLjU5LCDOtzJnID0gMC4wMDEsIG5vciB3YXMgdGhlcmUgYW4gaW50ZXJhY3Rpb24gb2YgcHJpbWUgcmFjZSB3aXRoIHRhcmdldCBvYmplY3QsIEYoMSw0NSkgPSAwLjAyMiwgcCA+IDAuODgsIM63MmcgPSAwLjAwMCkiLgoKT3BlbiB0aGUgYHJvdGVsbG9fc2hvb3Rlcl9leHAxLmNzdmAgZmlsZSBhbmQgdHJ5IHRvIHJlcHJvZHVjZSB0aGVpciByZXN1bHRzLiBSdW4gYW4gQU5PVkEgKHR5cGUgSUlJKSB3aXRoIGByZXNwYCBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGFuZCB0YXJnZXQsIHByaW1lLCBhbmQgdGhlaXIgaW50ZXJhY3Rpb24gYXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzLgoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQojIGxvYWQgdGhlIGdlbmVyYWwgZGF0YSBmaWxlCnJvdGVsbG9fZGF0YSA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJyb3RlbGxvX3Nob290ZXJfZXhwMS5jc3YiKSkKCiMgQU5PVkEKcm90ZWxsb19hb3YgPC0gYW92X2NhciAocmVzcCB+IHRhcmdldCpwcmltZSArCiAgICAgICAgICAgRXJyb3Ioc3ViamVjdC90YXJnZXQqcHJpbWUpLCBkYXRhID0gcm90ZWxsb19kYXRhKQpgYGAKCmBgYHtyIGVjaG89Rn0Ka25pdHI6OmthYmxlKG5pY2Uocm90ZWxsb19hb3YpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgoKCiMjIENvcnJlbGF0aW9uCgpIZXJlLCB3ZSB3YW50IHRvIGNoZWNrIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyBvbiB0aGUgYG5hcmNpc3Npc21fZGF0YWAuIEZpcnN0LCB3ZSBuZWVkIHRvIHJlbW92ZSBgc3ViamVjdGAgY29sdW1uIGJlY2F1c2UgaXQgaXMgbm90IG51bWVyaWM6CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpuYXJjaXNzaXNtX2RhdGFfY29yIDwtIG5hcmNpc3Npc21fZGF0YSAlPiUKICBzZWxlY3QoLXN1YmplY3QpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCBldmFsPUYsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMH0KCiMtLSBCYXNlIFI6CmNvcihuYXJjaXNzaXNtX2RhdGFfY29yLCBtZXRob2QgPSAicGVhcnNvbiIsICB1c2UgPSAiY29tcGxldGUub2JzIikKCiMtLSBQc3ljaCBsaWJyYXJ5Ogpwc3ljaDo6cGFpcnMucGFuZWxzKG5hcmNpc3Npc21fZGF0YV9jb3IsIG1ldGhvZCA9ICJwZWFyc29uIiwgaGlzdC5jb2wgPSAiIzAwQUZCQiIsIGRlbnNpdHkgPSBULCBlbGxpcHNlcyA9IEYsIHN0YXJzID0gVCkKCiMtLSBDb3JyZWxhdGlvbiBsaWJyYXJ5OgojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCiNsaWJyYXJ5KCJjb3JyZWxhdGlvbiIpCmNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihuYXJjaXNzaXNtX2RhdGFfY29yKSAlPiUgc3VtbWFyeSgpCgojLS0gYXBhVGFibGVzIGxpYnJhcnk6Cm5hcmNpc3Npc21fZGF0YV9jb3IgJT4lIAogIGFwYVRhYmxlczo6YXBhLmNvci50YWJsZShmaWxlbmFtZT0iLi9vdXRwdXRzL0Nvck1hdHJpeC5kb2MiLCBzaG93LmNvbmYuaW50ZXJ2YWw9VCkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQoKIy0tIEJhc2UgUjoKY29yKG5hcmNpc3Npc21fZGF0YV9jb3IsIG1ldGhvZCA9ICJwZWFyc29uIiwgIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRikKCiMtLSBQc3ljaCBsaWJyYXJ5Ogpwc3ljaDo6cGFpcnMucGFuZWxzKG5hcmNpc3Npc21fZGF0YV9jb3IsIG1ldGhvZCA9ICJwZWFyc29uIiwgaGlzdC5jb2wgPSAiIzAwQUZCQiIsIGRlbnNpdHkgPSBULCBlbGxpcHNlcyA9IEYsIHN0YXJzID0gVCkKCiMtLSBDb3JyZWxhdGlvbiBsaWJyYXJ5OgojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCiNsaWJyYXJ5KCJjb3JyZWxhdGlvbiIpCmNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihuYXJjaXNzaXNtX2RhdGFfY29yKSAlPiUgc3VtbWFyeSgpJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKQoKYGBgCgoKKiAqRXhlcmNpc2UqOiBQZW5ueWNvb2sgZXQgYWwuICgyMDIwKSBpbnZlc3RpZ2F0ZWQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFjdGl2ZWx5IG9wZW4tbWluZGVkIHRoaW5raW5nIHN0eWxlIGFib3V0IGV2aWRlbmNlIChBT1QtRSkgYW5kIGRpZmZlcmVudCBwb2xpdGljYWwsIHNjaWVudGlmaWMsIGFuZCByZWxpZ2lvdXMgYmVsaWVmcyAoc2VlIHRoZSBvcmlnaW5hbCBwYXBlciBbaGVyZV0oaHR0cHM6Ly9wc3lhcnhpdi5jb20vYTdrOTYpKS4gSW4gdGhlaXIgZmlyc3QgZXhwZXJpbWVudCwgdGhleSBjYWxjdWxhdGVkIHRoZSBjb3JyZWxhdGlvbiBvZiBBT1RFIGFuZCBzY2llbnRpZmljIGJlbGllZnMgaXRlbXMgKGdsb2JhbCB3YXJtaW5nLCBldm9sdXRpb24sIGV0Yy4pIGFuZCB0aGV5IGZvdW5kIHRoZSBmb2xsb3dpbmcgcmVzdWx0czoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJhZGFwdGVkIGZyb20gW1Blbm55Y29vayBldCBhbC4gKDIwMjApXShodHRwczovL3BzeWFyeGl2LmNvbS9hN2s5NikifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdwZW5ueWNvb2tfY29yci5wbmcnKSkKYGBgCgpPcGVuIHRoZSBgcGVubnljb29rX2FvdGVfZXhwMS5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cyBieSBjcmVhdGluZyB0aGUgc2FtZSBjb3JyZWxhdGlvbiBtYXRyaXguCgpgYGB7ciBtZXNzYWdlPUYsIGV2YWw9Rn0KcGVubnljb29rX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwicGVubnljb29rX2FvdGVfZXhwMS5jc3YiKSkgCgoKIy0tLS0tLS0tLS0gQmFzZSBSOgpjb3IocGVubnljb29rX2RhdGEsIG1ldGhvZCA9ICJwZWFyc29uIiwgIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKIy0tLS0tLS0tLS0gUHN5Y2ggbGlicmFyeToKcGVubnljb29rX2RhdGEgJT4lIAogIHBzeWNoOjpwYWlycy5wYW5lbHMobWV0aG9kID0gInBlYXJzb24iLCBoaXN0LmNvbCA9ICIjMDBBRkJCIiwgZGVuc2l0eSA9IFQsIGVsbGlwc2VzID0gRiwgc3RhcnMgPSBUKQoKIy0tLS0tLS0tLS0gQ29ycmVsYXRpb24gbGlicmFyeToKY29ycmVsYXRpb246OmNvcnJlbGF0aW9uKHBlbm55Y29va19kYXRhKSAlPiUgc3VtbWFyeSgpCgojLS0tLS0tLS0tLSBhcGFUYWJsZXMgbGlicmFyeToKcGVubnljb29rX2RhdGEgJT4lIAogIGFwYVRhYmxlczo6YXBhLmNvci50YWJsZShmaWxlbmFtZT0iLi9vdXRwdXRzL0Nvck1hdHJpeC5kb2MiLCBzaG93LmNvbmYuaW50ZXJ2YWw9VCkKYGBgCgoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpwZW5ueWNvb2tfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJwZW5ueWNvb2tfYW90ZV9leHAxLmNzdiIpKSAlPiUKICBjbGVhbl9uYW1lcygpCgpjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24ocGVubnljb29rX2RhdGEpICU+JSBzdW1tYXJ5KCkgJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQoKYGBgCgoKIyMgTGluZWFyIFJlZ3Jlc3Npb24KCkhlcmUsIHdlIGRvIHNpbmdsZSBhbmQgbXVsdGlwbGUgbGluZWFyIHJlZ3JlYXNzaW9uIG9uIHRoZSBgbmFyY2lzc2lzbV9kYXRhYDoKCmBgYHtyfQptMSA8LSBsbShtZW50YWxfaGVhbHRofm5hcmNpc3Npc20sIGRhdGE9IG5hcmNpc3Npc21fZGF0YSkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIGV2YWw9VCwgZWNobz1GLCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDB9CmJyb29tOjp0aWR5KG0xKSU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgpgYGB7cn0KbTIgPC0gbG0obWVudGFsX2hlYWx0aH5uYXJjaXNzaXNtK3BzeWNob3BhdGh5LCBkYXRhPSBuYXJjaXNzaXNtX2RhdGEpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpicm9vbTo6dGlkeShtMiklPiUKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCmBgYAoKKiAqRXhlcmNpc2UqOiBUcsOpbW9sacOocmUgYW5kIERqZXJpb3VhdCAoMjAyMCkgZXhhbWluZWQgdGhlIHJvbGUgb2YgKmNvZ25pdGl2ZSByZWZsZWN0aW9uKiBhbmQgKmJlbGllZiBpbiBzY2llbmNlKiBpbiBjbGltYXRlIGNoYW5nZSBza2VwdGljaXNtLiBJbiB0aGVpciBmaXJzdCBzdHVkeSwgdGhleSByZXZlYWxlZCB0aGF0IGNvZ25pdGl2ZSByZWZsZWN0aW9uIGFuZCBiZWxpZWYgaW4gc2NpZW5jZSBuZWdldGl2ZWx5IHByZWRpY3RlZCBjbGltYXRlIGNoYW5nZSBza2VwdGljaXNtIGV2ZW4gYWZ0ZXIgY29udHJvbGxpbmcgZm9yIGRlbW9ncmFwaGljIGFuZCBjb2duaXRpdmUgYWJpbGl0eSB2YXJpYWJsZXMgKHNlZSB0aGUgb3JpZ2luYWwgcGFwZXIgW2hlcmVdKGh0dHBzOi8vcHN5YXJ4aXYuY29tL3ZwOGs2LykpLiAKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJhZGFwdGVkIGZyb20gW1Ryw6ltb2xpw6hyZSBhbmQgRGplcmlvdWF0ICgyMDIwKV0oaHR0cHM6Ly9wc3lhcnhpdi5jb20vdnA4azYvKSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3RyZW1vbGllcmVfcmVnLnBuZycpKQpgYGAKCk9wZW4gdGhlIGB0cmVtb2xpZXJlX2RhdGFfZXhwMS5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cyBieSBydW5uaW5nIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24uIEVudGVyIGFnZSwgZ2VuZGVyLCBlZHVjYXRpb24sIGJlbGllZiBpbiBzY2llbmNlLCBsaXRlcmFjeSwgbnVtZXJhY3kgKE51bXRvdGFsKSwgYW5kIGNvZ25pdGl2ZSByZWZsZWN0aW9uIGFzIHByZWRpY3RvcnMgYW5kIGVudGVyIGNsaW1hdGUgY2hhbmdlIHNrZXB0aWNpc20gKGNsaW1hdG8pIGFzIHRoZSBvdXRjb21lIHZhcmlhYmxlLgoKYGBge3IgbWVzc2FnZT1GfQpUcmVtb2xpZXJlX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidHJlbW9saWVyZV9kYXRhX2V4cDEuY3N2IikpCgpUcmVtb2xpZXJlX3JlZz1sbShDbGltYXRvIH4gQWdlKyBHZW5kZXIrIEVkdWNhdGlvbisgQmVsaWVmSW5TY2llbmNldG90YWwrIExpdGVyYWN5KyBOdW10b3RhbCsgQ29nbml0aXZlUmVmbGVjdGlvbiwKICAgICAgICAgICAgICAgICAgICBkYXRhPVRyZW1vbGllcmVfZGF0YSkKYGBgCgoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpicm9vbTo6dGlkeShUcmVtb2xpZXJlX3JlZyklPiUKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgpnbGFuY2UoVHJlbW9saWVyZV9yZWcpJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQpgYGAKCgojIFJtYXJrZG93bgoKVG8gYmUgY29tcGxldGVkLi4uCgoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywncm1hcmtkb3duX3dpemFyZHMucG5nJykpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3JlcHJvZHVjaWJpbGl0eV9jb3VydC5wbmcnKSkKYGBgCgojIFJlZmVyZW5jZXMKCi0gR2hhc2VtaSwgTy4sIEhhbmRsZXksIFMuLCAmIEhvd2FydGgsIFMuICgyMDIwKS4gVGhlIEJyaWdodCBIb211bmN1bHVzIGluIG91ciBIZWFkOiBJbmRpdmlkdWFsIERpZmZlcmVuY2VzIGluIEludHVpdGl2ZSBTZW5zaXRpdml0eSB0byBMb2dpY2FsIFZhbGlkaXR5LgoKLSBKb2huLCBMLiBLLiwgSmVvbmcsIE0uLCBHaW5vLCBGLiwgJiBIdWFuZywgTC4gKDIwMTkpLiBUaGUgc2VsZi1wcmVzZW50YXRpb25hbCBjb25zZXF1ZW5jZXMgb2YgdXBob2xkaW5nIG9uZeKAmXMgc3RhbmNlIGluIHNwaXRlIG9mIHRoZSBldmlkZW5jZS4gT3JnYW5pemF0aW9uYWwgQmVoYXZpb3IgYW5kIEh1bWFuIERlY2lzaW9uIFByb2Nlc3NlcywgMTU0LCAxLTE0LgoKLSBQZW5ueWNvb2ssIEcuLCBDaGV5bmUsIEouIEEuLCBLb2VobGVyLCBELiBKLiwgJiBGdWdlbHNhbmcsIEouIEEuICgyMDIwKS4gT24gdGhlIGJlbGllZiB0aGF0IGJlbGllZnMgc2hvdWxkIGNoYW5nZSBhY2NvcmRpbmcgdG8gZXZpZGVuY2U6IEltcGxpY2F0aW9ucyBmb3IgY29uc3BpcmF0b3JpYWwsIG1vcmFsLCBwYXJhbm9ybWFsLCBwb2xpdGljYWwsIHJlbGlnaW91cywgYW5kIHNjaWVuY2UgYmVsaWVmcy4gSnVkZ21lbnQgYW5kIERlY2lzaW9uIE1ha2luZywgMTUoNCksIDQ3Ni4KCi0gUm90ZWxsbywgQy4gTS4sIEtlbGx5LCBMLiBKLiwgSGVpdCwgRS4sIFZhemlyZSwgUy4sICYgVnVsLCBFLiAoMjAxOCkuIFRoZSBTaGFwZSBvZiBST0MgQ3VydmVzIGluIFNob290ZXIgVGFza3M6IEltcGxpY2F0aW9ucyBmb3IgQmVzdCBQcmFjdGljZXMgaW4gQW5hbHlzaXMuIENvbGxhYnJhOiBQc3ljaG9sb2d5LCA0KDEpLgoKLSBUcsOpbW9sacOocmUsIEIuLCAmIERqZXJpb3VhdCwgSC4gKDIwMjApLiBEb27igJl0IHlvdSBzZWUgdGhhdCBpdHMgY29sZCEgRXhwbG9yaW5nIHRoZSByb2xlcyBvZiBjb2duaXRpdmUgcmVmbGVjdGlvbiwgY2xpbWF0ZSBzY2llbmNlIGxpdGVyYWN5LCBpbGx1c2lvbiBvZiBrbm93bGVkZ2UsIGFuZCBwb2xpdGljYWwgb3JpZW50YXRpb24gaW4gY2xpbWF0ZSBjaGFuZ2Ugc2tlcHRpY2lzbS4KCi0gV2lja2hhbSwgSC4gKDIwMTQpLiBUaWR5IGRhdGEuIEpvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmUsIDU5KDEwKSwgMS0yMy4=